webfuscator 1.3.0 → 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 (180) hide show
  1. package/DISCLOSURE +9 -0
  2. package/README.md +67 -0
  3. package/dist/analysis/constant.d.ts.map +1 -1
  4. package/dist/analysis/constant.js +10 -26
  5. package/dist/analysis/constant.js.map +1 -1
  6. package/dist/analysis/inlineability.d.ts.map +1 -1
  7. package/dist/analysis/inlineability.js +4 -12
  8. package/dist/analysis/inlineability.js.map +1 -1
  9. package/dist/analysis/purity.d.ts +3 -3
  10. package/dist/analysis/purity.d.ts.map +1 -1
  11. package/dist/analysis/purity.js +28 -75
  12. package/dist/analysis/purity.js.map +1 -1
  13. package/dist/preparation/expand-destructuring.d.ts +2 -3
  14. package/dist/preparation/expand-destructuring.d.ts.map +1 -1
  15. package/dist/preparation/expand-destructuring.js +2 -8
  16. package/dist/preparation/expand-destructuring.js.map +1 -1
  17. package/dist/transforms/arguments-to-parameters.d.ts +5 -4
  18. package/dist/transforms/arguments-to-parameters.d.ts.map +1 -1
  19. package/dist/transforms/arguments-to-parameters.js +5 -4
  20. package/dist/transforms/arguments-to-parameters.js.map +1 -1
  21. package/dist/transforms/arrow-to-function.d.ts +4 -3
  22. package/dist/transforms/arrow-to-function.d.ts.map +1 -1
  23. package/dist/transforms/arrow-to-function.js +6 -8
  24. package/dist/transforms/arrow-to-function.js.map +1 -1
  25. package/dist/transforms/collapse-single-use-temps.d.ts +2 -1
  26. package/dist/transforms/collapse-single-use-temps.d.ts.map +1 -1
  27. package/dist/transforms/collapse-single-use-temps.js +12 -20
  28. package/dist/transforms/collapse-single-use-temps.js.map +1 -1
  29. package/dist/transforms/conceal-strings.d.ts +1 -2
  30. package/dist/transforms/conceal-strings.d.ts.map +1 -1
  31. package/dist/transforms/conceal-strings.js +2 -7
  32. package/dist/transforms/conceal-strings.js.map +1 -1
  33. package/dist/transforms/const-let-to-var.d.ts +4 -3
  34. package/dist/transforms/const-let-to-var.d.ts.map +1 -1
  35. package/dist/transforms/const-let-to-var.js +8 -94
  36. package/dist/transforms/const-let-to-var.js.map +1 -1
  37. package/dist/transforms/const-to-let.d.ts +1 -2
  38. package/dist/transforms/const-to-let.d.ts.map +1 -1
  39. package/dist/transforms/const-to-let.js +1 -6
  40. package/dist/transforms/const-to-let.js.map +1 -1
  41. package/dist/transforms/drop-console.d.ts.map +1 -1
  42. package/dist/transforms/drop-console.js +3 -5
  43. package/dist/transforms/drop-console.js.map +1 -1
  44. package/dist/transforms/expand-binary-assignment.d.ts.map +1 -1
  45. package/dist/transforms/expand-binary-assignment.js +0 -5
  46. package/dist/transforms/expand-binary-assignment.js.map +1 -1
  47. package/dist/transforms/expand-logical-assignment.d.ts.map +1 -1
  48. package/dist/transforms/expand-logical-assignment.js +0 -5
  49. package/dist/transforms/expand-logical-assignment.js.map +1 -1
  50. package/dist/transforms/extract-object-properties.js +5 -12
  51. package/dist/transforms/extract-object-properties.js.map +1 -1
  52. package/dist/transforms/fold-builtin-methods.js +3 -11
  53. package/dist/transforms/fold-builtin-methods.js.map +1 -1
  54. package/dist/transforms/fold-constants.js +2 -2
  55. package/dist/transforms/fold-constants.js.map +1 -1
  56. package/dist/transforms/function-declaration-to-expression.d.ts +5 -6
  57. package/dist/transforms/function-declaration-to-expression.d.ts.map +1 -1
  58. package/dist/transforms/function-declaration-to-expression.js +5 -6
  59. package/dist/transforms/function-declaration-to-expression.js.map +1 -1
  60. package/dist/transforms/hoist-variables.d.ts +1 -2
  61. package/dist/transforms/hoist-variables.d.ts.map +1 -1
  62. package/dist/transforms/hoist-variables.js +2 -11
  63. package/dist/transforms/hoist-variables.js.map +1 -1
  64. package/dist/transforms/inline-functions.js +11 -39
  65. package/dist/transforms/inline-functions.js.map +1 -1
  66. package/dist/transforms/logical-to-ternary.d.ts +1 -1
  67. package/dist/transforms/logical-to-ternary.js +5 -9
  68. package/dist/transforms/logical-to-ternary.js.map +1 -1
  69. package/dist/transforms/mangle-properties.d.ts +2 -5
  70. package/dist/transforms/mangle-properties.d.ts.map +1 -1
  71. package/dist/transforms/mangle-properties.js +2 -5
  72. package/dist/transforms/mangle-properties.js.map +1 -1
  73. package/dist/transforms/nullish-coalescing-to-ternary.d.ts +4 -5
  74. package/dist/transforms/nullish-coalescing-to-ternary.d.ts.map +1 -1
  75. package/dist/transforms/nullish-coalescing-to-ternary.js +7 -12
  76. package/dist/transforms/nullish-coalescing-to-ternary.js.map +1 -1
  77. package/dist/transforms/object-method-to-property.d.ts +1 -1
  78. package/dist/transforms/object-method-to-property.js +1 -1
  79. package/dist/transforms/optional-chaining-to-ternary.d.ts +4 -3
  80. package/dist/transforms/optional-chaining-to-ternary.d.ts.map +1 -1
  81. package/dist/transforms/optional-chaining-to-ternary.js +9 -9
  82. package/dist/transforms/optional-chaining-to-ternary.js.map +1 -1
  83. package/dist/transforms/pack-declarations-into-parameters.d.ts.map +1 -1
  84. package/dist/transforms/pack-declarations-into-parameters.js +4 -16
  85. package/dist/transforms/pack-declarations-into-parameters.js.map +1 -1
  86. package/dist/transforms/pack.d.ts +4 -11
  87. package/dist/transforms/pack.d.ts.map +1 -1
  88. package/dist/transforms/pack.js +4 -17
  89. package/dist/transforms/pack.js.map +1 -1
  90. package/dist/transforms/remove-anonymous-function-names.d.ts +2 -4
  91. package/dist/transforms/remove-anonymous-function-names.d.ts.map +1 -1
  92. package/dist/transforms/remove-anonymous-function-names.js +2 -40
  93. package/dist/transforms/remove-anonymous-function-names.js.map +1 -1
  94. package/dist/transforms/remove-unreachable-code.js +2 -21
  95. package/dist/transforms/remove-unreachable-code.js.map +1 -1
  96. package/dist/transforms/remove-unused-code.d.ts.map +1 -1
  97. package/dist/transforms/remove-unused-code.js +5 -10
  98. package/dist/transforms/remove-unused-code.js.map +1 -1
  99. package/dist/transforms/rename-identifiers.d.ts +3 -3
  100. package/dist/transforms/rename-identifiers.d.ts.map +1 -1
  101. package/dist/transforms/rename-identifiers.js +4 -20
  102. package/dist/transforms/rename-identifiers.js.map +1 -1
  103. package/dist/transforms/reorder-function-declarations.d.ts +1 -2
  104. package/dist/transforms/reorder-function-declarations.d.ts.map +1 -1
  105. package/dist/transforms/reorder-function-declarations.js +2 -4
  106. package/dist/transforms/reorder-function-declarations.js.map +1 -1
  107. package/dist/transforms/specials-to-strings.d.ts.map +1 -1
  108. package/dist/transforms/specials-to-strings.js +2 -4
  109. package/dist/transforms/specials-to-strings.js.map +1 -1
  110. package/dist/transforms/switch-to-if.js +5 -15
  111. package/dist/transforms/switch-to-if.js.map +1 -1
  112. package/dist/transforms/ternary-to-if.d.ts.map +1 -1
  113. package/dist/transforms/ternary-to-if.js +5 -9
  114. package/dist/transforms/ternary-to-if.js.map +1 -1
  115. package/dist/transforms/update-to-assignment.d.ts.map +1 -1
  116. package/dist/transforms/update-to-assignment.js +6 -8
  117. package/dist/transforms/update-to-assignment.js.map +1 -1
  118. package/dist/transforms/yodify-conditions.d.ts +3 -4
  119. package/dist/transforms/yodify-conditions.d.ts.map +1 -1
  120. package/dist/transforms/yodify-conditions.js +3 -4
  121. package/dist/transforms/yodify-conditions.js.map +1 -1
  122. package/dist/utils/ast.d.ts +0 -1
  123. package/dist/utils/ast.d.ts.map +1 -1
  124. package/dist/utils/ast.js +0 -26
  125. package/dist/utils/ast.js.map +1 -1
  126. package/dist/utils/evaluation-order.js +1 -1
  127. package/dist/utils/evaluation-order.js.map +1 -1
  128. package/dist/utils/logical-lowering.d.ts.map +1 -1
  129. package/dist/utils/logical-lowering.js +7 -9
  130. package/dist/utils/logical-lowering.js.map +1 -1
  131. package/dist/utils/nullish.d.ts +4 -0
  132. package/dist/utils/nullish.d.ts.map +1 -0
  133. package/dist/utils/nullish.js +8 -0
  134. package/dist/utils/nullish.js.map +1 -0
  135. package/dist/utils/paths.d.ts +0 -2
  136. package/dist/utils/paths.d.ts.map +1 -1
  137. package/dist/utils/paths.js +0 -28
  138. package/dist/utils/paths.js.map +1 -1
  139. package/package.json +16 -10
  140. package/src/analysis/constant.ts +12 -30
  141. package/src/analysis/inlineability.ts +4 -12
  142. package/src/analysis/purity.ts +29 -92
  143. package/src/preparation/expand-destructuring.ts +2 -10
  144. package/src/transforms/arguments-to-parameters.ts +5 -4
  145. package/src/transforms/arrow-to-function.ts +6 -8
  146. package/src/transforms/collapse-single-use-temps.ts +12 -23
  147. package/src/transforms/conceal-strings.ts +3 -8
  148. package/src/transforms/const-let-to-var.ts +9 -100
  149. package/src/transforms/const-to-let.ts +1 -6
  150. package/src/transforms/drop-console.ts +3 -5
  151. package/src/transforms/expand-binary-assignment.ts +0 -5
  152. package/src/transforms/expand-logical-assignment.ts +0 -5
  153. package/src/transforms/extract-object-properties.ts +5 -14
  154. package/src/transforms/fold-builtin-methods.ts +3 -12
  155. package/src/transforms/fold-constants.ts +2 -2
  156. package/src/transforms/function-declaration-to-expression.ts +5 -6
  157. package/src/transforms/hoist-variables.ts +2 -12
  158. package/src/transforms/inline-functions.ts +16 -46
  159. package/src/transforms/logical-to-ternary.ts +5 -10
  160. package/src/transforms/mangle-properties.ts +2 -5
  161. package/src/transforms/nullish-coalescing-to-ternary.ts +7 -13
  162. package/src/transforms/object-method-to-property.ts +1 -1
  163. package/src/transforms/optional-chaining-to-ternary.ts +9 -9
  164. package/src/transforms/pack-declarations-into-parameters.ts +10 -16
  165. package/src/transforms/pack.ts +4 -17
  166. package/src/transforms/remove-anonymous-function-names.ts +3 -46
  167. package/src/transforms/remove-unreachable-code.ts +2 -22
  168. package/src/transforms/remove-unused-code.ts +5 -10
  169. package/src/transforms/rename-identifiers.ts +4 -20
  170. package/src/transforms/reorder-function-declarations.ts +2 -4
  171. package/src/transforms/specials-to-strings.ts +2 -4
  172. package/src/transforms/switch-to-if.ts +5 -22
  173. package/src/transforms/ternary-to-if.ts +5 -9
  174. package/src/transforms/update-to-assignment.ts +6 -8
  175. package/src/transforms/yodify-conditions.ts +3 -4
  176. package/src/utils/ast.ts +0 -29
  177. package/src/utils/evaluation-order.ts +1 -1
  178. package/src/utils/logical-lowering.ts +7 -11
  179. package/src/utils/nullish.ts +23 -0
  180. package/src/utils/paths.ts +0 -32
@@ -3,7 +3,7 @@ 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 { hasAnnexBFunctionAlias, referencesOrWritesVariable } from '../utils/paths'
6
+ import { referencesOrWritesVariable } from '../utils/paths'
7
7
 
8
8
  export interface InlineCandidate {
9
9
  name: string
@@ -30,10 +30,6 @@ export function analyzeInlineability(ast: File): Map<string, InlineCandidate> {
30
30
  if (!path.node.id) {
31
31
  return
32
32
  }
33
- // Babel misses the sloppy Annex B `var` alias of a block-level function.
34
- if (hasAnnexBFunctionAlias(path)) {
35
- return
36
- }
37
33
  const binding = path.scope.getBinding(path.node.id.name)
38
34
  if (!binding) {
39
35
  return
@@ -131,9 +127,9 @@ function checkCandidate(
131
127
  if (!t.isBlockStatement(fnNode.body)) {
132
128
  return null
133
129
  }
134
- // Writes live in `constantViolations`, not `referencePaths`. Any write can
135
- // make a call resolve to a different function.
136
- if (binding.constantViolations.length > 0) {
130
+ // Writes to mutable bindings live in `constantViolations`, not
131
+ // `referencePaths`, and can make a call resolve to a different function.
132
+ if (binding.kind !== 'const' && binding.constantViolations.length > 0) {
137
133
  return null
138
134
  }
139
135
  // Splicing loses a named function expression's inner binding.
@@ -273,10 +269,6 @@ const analyzeBodyVisitor: Visitor<AnalyzeBodyState> = {
273
269
  path.stop()
274
270
  }
275
271
  },
276
- WithStatement(path, state) {
277
- state.unsafe = true
278
- path.stop()
279
- },
280
272
  Identifier(path, state) {
281
273
  const name = path.node.name
282
274
  if (state.innerFnDepth === 0 && name === 'arguments') {
@@ -1,18 +1,11 @@
1
- import type { Binding, NodePath, Scope } from '@babel/traverse'
1
+ import type { Scope } from '@babel/traverse'
2
2
  import * as t from '@babel/types'
3
3
 
4
- import { isInsideWith } from '../utils/ast'
5
4
  import { isPrimitiveLiteral } from '../utils/literal'
6
- import {
7
- declarationReaches,
8
- initializerReaches,
9
- readCrossesFunctionBoundary,
10
- } from './document-order'
11
5
 
12
6
  // Purity also requires deterministic repetition and no throw. Identifier reads
13
- // need scope and reference paths to prove resolution, initialization, and the
14
- // absence of `with` interception.
15
- export function isPure(node: t.Node, scope?: Scope, referencePath?: NodePath): boolean {
7
+ // need scope to prove resolution.
8
+ export function isPure(node: t.Node, scope?: Scope): boolean {
16
9
  if (
17
10
  t.isNumericLiteral(node) ||
18
11
  t.isStringLiteral(node) ||
@@ -25,7 +18,7 @@ export function isPure(node: t.Node, scope?: Scope, referencePath?: NodePath): b
25
18
  return true
26
19
  }
27
20
  if (t.isIdentifier(node)) {
28
- return identifierReadIsSafe(node, scope, referencePath)
21
+ return identifierReadIsSafe(node, scope)
29
22
  }
30
23
  if (t.isTemplateLiteral(node)) {
31
24
  // ToString can call object code or throw on Symbol.
@@ -39,7 +32,7 @@ export function isPure(node: t.Node, scope?: Scope, referencePath?: NodePath): b
39
32
  if (t.isUnaryExpression(node)) {
40
33
  // Numeric unary operators invoke ToPrimitive. `delete` and `throw` act.
41
34
  if (node.operator === '!' || node.operator === 'typeof' || node.operator === 'void') {
42
- return isPure(node.argument, scope, referencePath)
35
+ return isPure(node.argument, scope)
43
36
  }
44
37
  return false
45
38
  }
@@ -47,7 +40,7 @@ export function isPure(node: t.Node, scope?: Scope, referencePath?: NodePath): b
47
40
  if (!t.isExpression(node.left)) {
48
41
  return false
49
42
  }
50
- if (!isPure(node.left, scope, referencePath) || !isPure(node.right, scope, referencePath)) {
43
+ if (!isPure(node.left, scope) || !isPure(node.right, scope)) {
51
44
  return false
52
45
  }
53
46
  // Only strict equality avoids coercion. Other operators need safe literals.
@@ -57,18 +50,16 @@ export function isPure(node: t.Node, scope?: Scope, referencePath?: NodePath): b
57
50
  return coercesWithoutEffect(node.left, node.right, node.operator)
58
51
  }
59
52
  if (t.isLogicalExpression(node)) {
60
- return isPure(node.left, scope, referencePath) && isPure(node.right, scope, referencePath)
53
+ return isPure(node.left, scope) && isPure(node.right, scope)
61
54
  }
62
55
  if (t.isConditionalExpression(node)) {
63
56
  return (
64
- isPure(node.test, scope, referencePath) &&
65
- isPure(node.consequent, scope, referencePath) &&
66
- isPure(node.alternate, scope, referencePath)
57
+ isPure(node.test, scope) && isPure(node.consequent, scope) && isPure(node.alternate, scope)
67
58
  )
68
59
  }
69
60
  if (t.isSequenceExpression(node)) {
70
61
  for (const expr of node.expressions) {
71
- if (!isPure(expr, scope, referencePath)) {
62
+ if (!isPure(expr, scope)) {
72
63
  return false
73
64
  }
74
65
  }
@@ -77,8 +68,9 @@ export function isPure(node: t.Node, scope?: Scope, referencePath?: NodePath): b
77
68
  return false
78
69
  }
79
70
 
80
- // A droppable identifier read must resolve after binding initialization.
81
- export function isSideEffectFree(node: t.Node, scope?: Scope, referencePath?: NodePath): boolean {
71
+ // A droppable identifier read must resolve to a local binding or immutable
72
+ // global `undefined`.
73
+ export function isSideEffectFree(node: t.Node, scope?: Scope): boolean {
82
74
  if (
83
75
  t.isNumericLiteral(node) ||
84
76
  t.isStringLiteral(node) ||
@@ -99,7 +91,7 @@ export function isSideEffectFree(node: t.Node, scope?: Scope, referencePath?: No
99
91
  return true
100
92
  }
101
93
  if (t.isIdentifier(node)) {
102
- return identifierReadIsSafe(node, scope, referencePath)
94
+ return identifierReadIsSafe(node, scope)
103
95
  }
104
96
  if (t.isFunctionExpression(node) || t.isArrowFunctionExpression(node)) {
105
97
  return true
@@ -107,7 +99,7 @@ export function isSideEffectFree(node: t.Node, scope?: Scope, referencePath?: No
107
99
  if (t.isClassExpression(node)) {
108
100
  // `extends` and computed keys run during class definition.
109
101
  if (node.superClass) {
110
- if (!isSideEffectFree(node.superClass, scope, referencePath)) {
102
+ if (!isSideEffectFree(node.superClass, scope)) {
111
103
  return false
112
104
  }
113
105
  // ClassDefinitionEvaluation accepts only `null` or a constructor.
@@ -127,7 +119,7 @@ export function isSideEffectFree(node: t.Node, scope?: Scope, referencePath?: No
127
119
  'computed' in member &&
128
120
  member.computed &&
129
121
  'key' in member &&
130
- !isSideEffectFree(member.key, scope, referencePath)
122
+ !isSideEffectFree(member.key, scope)
131
123
  ) {
132
124
  return false
133
125
  }
@@ -142,7 +134,7 @@ export function isSideEffectFree(node: t.Node, scope?: Scope, referencePath?: No
142
134
  if (t.isSpreadElement(element)) {
143
135
  return false
144
136
  }
145
- if (!isSideEffectFree(element, scope, referencePath)) {
137
+ if (!isSideEffectFree(element, scope)) {
146
138
  return false
147
139
  }
148
140
  }
@@ -154,7 +146,7 @@ export function isSideEffectFree(node: t.Node, scope?: Scope, referencePath?: No
154
146
  return false
155
147
  }
156
148
  if (t.isObjectMethod(prop)) {
157
- if (prop.computed && !isSideEffectFree(prop.key, scope, referencePath)) {
149
+ if (prop.computed && !isSideEffectFree(prop.key, scope)) {
158
150
  return false
159
151
  }
160
152
  continue
@@ -162,13 +154,13 @@ export function isSideEffectFree(node: t.Node, scope?: Scope, referencePath?: No
162
154
  if (!t.isObjectProperty(prop)) {
163
155
  return false
164
156
  }
165
- if (prop.computed && !isSideEffectFree(prop.key, scope, referencePath)) {
157
+ if (prop.computed && !isSideEffectFree(prop.key, scope)) {
166
158
  return false
167
159
  }
168
160
  if (!t.isExpression(prop.value)) {
169
161
  return false
170
162
  }
171
- if (!isSideEffectFree(prop.value, scope, referencePath)) {
163
+ if (!isSideEffectFree(prop.value, scope)) {
172
164
  return false
173
165
  }
174
166
  }
@@ -179,7 +171,7 @@ export function isSideEffectFree(node: t.Node, scope?: Scope, referencePath?: No
179
171
  return false
180
172
  }
181
173
  if (node.operator === '!' || node.operator === 'typeof' || node.operator === 'void') {
182
- return isSideEffectFree(node.argument, scope, referencePath)
174
+ return isSideEffectFree(node.argument, scope)
183
175
  }
184
176
  return false
185
177
  }
@@ -187,10 +179,7 @@ export function isSideEffectFree(node: t.Node, scope?: Scope, referencePath?: No
187
179
  if (!t.isExpression(node.left)) {
188
180
  return false
189
181
  }
190
- if (
191
- !isSideEffectFree(node.left, scope, referencePath) ||
192
- !isSideEffectFree(node.right, scope, referencePath)
193
- ) {
182
+ if (!isSideEffectFree(node.left, scope) || !isSideEffectFree(node.right, scope)) {
194
183
  return false
195
184
  }
196
185
  // Coercing operators need compatible literals that cannot throw.
@@ -200,21 +189,18 @@ export function isSideEffectFree(node: t.Node, scope?: Scope, referencePath?: No
200
189
  return coercesWithoutEffect(node.left, node.right, node.operator)
201
190
  }
202
191
  if (t.isLogicalExpression(node)) {
203
- return (
204
- isSideEffectFree(node.left, scope, referencePath) &&
205
- isSideEffectFree(node.right, scope, referencePath)
206
- )
192
+ return isSideEffectFree(node.left, scope) && isSideEffectFree(node.right, scope)
207
193
  }
208
194
  if (t.isConditionalExpression(node)) {
209
195
  return (
210
- isSideEffectFree(node.test, scope, referencePath) &&
211
- isSideEffectFree(node.consequent, scope, referencePath) &&
212
- isSideEffectFree(node.alternate, scope, referencePath)
196
+ isSideEffectFree(node.test, scope) &&
197
+ isSideEffectFree(node.consequent, scope) &&
198
+ isSideEffectFree(node.alternate, scope)
213
199
  )
214
200
  }
215
201
  if (t.isSequenceExpression(node)) {
216
202
  for (const expr of node.expressions) {
217
- if (!isSideEffectFree(expr, scope, referencePath)) {
203
+ if (!isSideEffectFree(expr, scope)) {
218
204
  return false
219
205
  }
220
206
  }
@@ -223,43 +209,13 @@ export function isSideEffectFree(node: t.Node, scope?: Scope, referencePath?: No
223
209
  return false
224
210
  }
225
211
 
226
- // A safe read needs a resolved, initialized binding outside `with`. Unbound
227
- // `undefined` is safe because the global property is immutable.
228
- function identifierReadIsSafe(
229
- node: t.Identifier,
230
- scope: Scope | undefined,
231
- referencePath: NodePath | undefined,
232
- ): boolean {
233
- // A `with` property can shadow any name, including `undefined`.
234
- if (referencePath && isInsideWith(referencePath)) {
235
- return false
236
- }
212
+ // Unbound `undefined` is safe because the global property is immutable.
213
+ function identifierReadIsSafe(node: t.Identifier, scope: Scope | undefined): boolean {
237
214
  const isUndefinedName = node.name === 'undefined'
238
215
  if (!scope) {
239
216
  return isUndefinedName
240
217
  }
241
- const binding = scope.getBinding(node.name)
242
- if (!binding) {
243
- return isUndefinedName
244
- }
245
- if (t.isFunctionDeclaration(binding.path.node) || binding.kind === 'module') {
246
- return true
247
- }
248
- if (binding.kind === 'param') {
249
- // Earlier parameter defaults can read later parameters in TDZ.
250
- if (!referencePath || !readIsInOwnParamList(binding, referencePath)) {
251
- return true
252
- }
253
- return declarationReaches(binding.path, referencePath)
254
- }
255
- // Lexical reads need an unconditionally reached declaration in the same call.
256
- if (isLexicalBinding(binding)) {
257
- if (referencePath && readCrossesFunctionBoundary(binding, referencePath)) {
258
- return false
259
- }
260
- return initializerReaches(binding.path, referencePath)
261
- }
262
- return declarationReaches(binding.path, referencePath)
218
+ return scope.getBinding(node.name) !== undefined || isUndefinedName
263
219
  }
264
220
 
265
221
  // Only `null` and plain function expressions are known-valid superclass values.
@@ -294,22 +250,3 @@ function coercesWithoutEffect(left: t.Node, right: t.Node, operator: string): bo
294
250
  }
295
251
  return leftKind !== 'bigint' || !THROWING_BIGINT_OPERATORS.has(operator)
296
252
  }
297
-
298
- function isLexicalBinding(binding: Binding): boolean {
299
- return (
300
- binding.kind === 'let' || binding.kind === 'const' || t.isClassDeclaration(binding.path.node)
301
- )
302
- }
303
-
304
- function readIsInOwnParamList(binding: Binding, referencePath: NodePath): boolean {
305
- const owner = binding.scope.block
306
- let current: NodePath | null = referencePath
307
- while (current) {
308
- const parent: NodePath | null = current.parentPath
309
- if (parent && parent.node === owner && current.listKey === 'params') {
310
- return true
311
- }
312
- current = parent
313
- }
314
- return false
315
- }
@@ -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 { 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
- * 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.
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
- // A `with` property can capture the generated temporary.
46
- if (isInsideWith(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 { 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,9 +64,7 @@ const visitor: Visitor<ChangeState> = {
64
64
  !valueStaysInView(path) ||
65
65
  arrowReferencesSuper(path) ||
66
66
  inUnsafeCaptureContext(path) ||
67
- usesReassignedArguments(path) ||
68
- // `with` can capture Babel's `this` or `arguments` alias.
69
- isInsideWith(path)
67
+ usesReassignedArguments(path)
70
68
  ) {
71
69
  return
72
70
  }
@@ -140,7 +138,7 @@ function valueStaysInView(valuePath: NodePath): boolean {
140
138
  return false
141
139
  }
142
140
  const binding = valuePath.scope.getBinding(parent.node.id.name)
143
- if (!binding || !binding.constant) {
141
+ if (!binding || (binding.kind !== 'const' && !binding.constant)) {
144
142
  return false
145
143
  }
146
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
  }
@@ -20,8 +20,7 @@ const RECORD_WIDTH = 4
20
20
  * `scope: 'program'` creates one vault. `scope: 'function'` creates a vault per
21
21
  * function but repeats the program-wide pool in each one. Parameter defaults and
22
22
  * computed keys use the enclosing vault because they run before the function
23
- * body. Code inside `with` gets a lexical vault so the object cannot capture
24
- * decoder references.
23
+ * body.
25
24
  *
26
25
  * Directives, module specifiers, import attributes, and non-computed keys are not
27
26
  * runtime expressions, so they stay unchanged.
@@ -91,7 +90,7 @@ interface ResolvedOptions {
91
90
  scope: 'function' | 'program'
92
91
  }
93
92
 
94
- type VaultOwner = t.Program | t.Function | t.BlockStatement
93
+ type VaultOwner = t.Program | t.Function
95
94
  type VaultOwnerPath = NodePath<VaultOwner>
96
95
 
97
96
  interface Occurrence {
@@ -208,10 +207,6 @@ function findVaultOwner(
208
207
  if (current.isFunction() && child.key === 'body' && scope === 'function') {
209
208
  return current as VaultOwnerPath
210
209
  }
211
- if (current.isWithStatement() && child.key === 'body') {
212
- const body = current.get('body')
213
- return body.isBlockStatement() ? (body as VaultOwnerPath) : null
214
- }
215
210
  if (current.isProgram()) {
216
211
  return current
217
212
  }
@@ -375,7 +370,7 @@ function maskPosition(position: number, key: number, step: number, indexMask: nu
375
370
  }
376
371
 
377
372
  function statementBody(owner: VaultOwnerPath): NodePath<t.Program | t.BlockStatement> {
378
- if (owner.isProgram() || owner.isBlockStatement()) {
373
+ if (owner.isProgram()) {
379
374
  return owner
380
375
  }
381
376
  const body = owner.get('body') as NodePath