webfuscator 1.3.0 → 1.3.2
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.
- package/DISCLOSURE +9 -0
- package/README.md +67 -0
- package/dist/analysis/constant.d.ts.map +1 -1
- package/dist/analysis/constant.js +10 -26
- package/dist/analysis/constant.js.map +1 -1
- package/dist/analysis/inlineability.d.ts.map +1 -1
- package/dist/analysis/inlineability.js +4 -12
- package/dist/analysis/inlineability.js.map +1 -1
- package/dist/analysis/purity.d.ts +3 -3
- package/dist/analysis/purity.d.ts.map +1 -1
- package/dist/analysis/purity.js +28 -75
- package/dist/analysis/purity.js.map +1 -1
- package/dist/preparation/expand-destructuring.d.ts +2 -3
- package/dist/preparation/expand-destructuring.d.ts.map +1 -1
- package/dist/preparation/expand-destructuring.js +2 -8
- package/dist/preparation/expand-destructuring.js.map +1 -1
- package/dist/transforms/arguments-to-parameters.d.ts +5 -4
- package/dist/transforms/arguments-to-parameters.d.ts.map +1 -1
- package/dist/transforms/arguments-to-parameters.js +5 -4
- package/dist/transforms/arguments-to-parameters.js.map +1 -1
- package/dist/transforms/arrow-to-function.d.ts +4 -3
- package/dist/transforms/arrow-to-function.d.ts.map +1 -1
- package/dist/transforms/arrow-to-function.js +6 -8
- package/dist/transforms/arrow-to-function.js.map +1 -1
- package/dist/transforms/collapse-single-use-temps.d.ts +2 -1
- package/dist/transforms/collapse-single-use-temps.d.ts.map +1 -1
- package/dist/transforms/collapse-single-use-temps.js +12 -20
- package/dist/transforms/collapse-single-use-temps.js.map +1 -1
- package/dist/transforms/conceal-strings.d.ts +1 -2
- package/dist/transforms/conceal-strings.d.ts.map +1 -1
- package/dist/transforms/conceal-strings.js +2 -7
- package/dist/transforms/conceal-strings.js.map +1 -1
- package/dist/transforms/const-let-to-var.d.ts +4 -3
- package/dist/transforms/const-let-to-var.d.ts.map +1 -1
- package/dist/transforms/const-let-to-var.js +8 -94
- package/dist/transforms/const-let-to-var.js.map +1 -1
- package/dist/transforms/const-to-let.d.ts +1 -2
- package/dist/transforms/const-to-let.d.ts.map +1 -1
- package/dist/transforms/const-to-let.js +1 -6
- package/dist/transforms/const-to-let.js.map +1 -1
- package/dist/transforms/drop-console.d.ts.map +1 -1
- package/dist/transforms/drop-console.js +3 -5
- package/dist/transforms/drop-console.js.map +1 -1
- package/dist/transforms/expand-binary-assignment.d.ts.map +1 -1
- package/dist/transforms/expand-binary-assignment.js +0 -5
- package/dist/transforms/expand-binary-assignment.js.map +1 -1
- package/dist/transforms/expand-logical-assignment.d.ts.map +1 -1
- package/dist/transforms/expand-logical-assignment.js +0 -5
- package/dist/transforms/expand-logical-assignment.js.map +1 -1
- package/dist/transforms/extract-object-properties.js +5 -12
- package/dist/transforms/extract-object-properties.js.map +1 -1
- package/dist/transforms/fold-builtin-methods.js +3 -11
- package/dist/transforms/fold-builtin-methods.js.map +1 -1
- package/dist/transforms/fold-constants.js +2 -2
- package/dist/transforms/fold-constants.js.map +1 -1
- package/dist/transforms/function-declaration-to-expression.d.ts +5 -6
- package/dist/transforms/function-declaration-to-expression.d.ts.map +1 -1
- package/dist/transforms/function-declaration-to-expression.js +5 -6
- package/dist/transforms/function-declaration-to-expression.js.map +1 -1
- package/dist/transforms/hoist-variables.d.ts +1 -2
- package/dist/transforms/hoist-variables.d.ts.map +1 -1
- package/dist/transforms/hoist-variables.js +2 -11
- package/dist/transforms/hoist-variables.js.map +1 -1
- package/dist/transforms/inline-functions.js +11 -39
- package/dist/transforms/inline-functions.js.map +1 -1
- package/dist/transforms/logical-to-ternary.d.ts +1 -1
- package/dist/transforms/logical-to-ternary.js +5 -9
- package/dist/transforms/logical-to-ternary.js.map +1 -1
- package/dist/transforms/mangle-properties.d.ts +2 -5
- package/dist/transforms/mangle-properties.d.ts.map +1 -1
- package/dist/transforms/mangle-properties.js +2 -5
- package/dist/transforms/mangle-properties.js.map +1 -1
- package/dist/transforms/nullish-coalescing-to-ternary.d.ts +4 -5
- package/dist/transforms/nullish-coalescing-to-ternary.d.ts.map +1 -1
- package/dist/transforms/nullish-coalescing-to-ternary.js +7 -12
- package/dist/transforms/nullish-coalescing-to-ternary.js.map +1 -1
- package/dist/transforms/object-method-to-property.d.ts +1 -1
- package/dist/transforms/object-method-to-property.js +1 -1
- package/dist/transforms/optional-chaining-to-ternary.d.ts +4 -3
- package/dist/transforms/optional-chaining-to-ternary.d.ts.map +1 -1
- package/dist/transforms/optional-chaining-to-ternary.js +9 -9
- package/dist/transforms/optional-chaining-to-ternary.js.map +1 -1
- package/dist/transforms/pack-declarations-into-parameters.d.ts.map +1 -1
- package/dist/transforms/pack-declarations-into-parameters.js +4 -16
- package/dist/transforms/pack-declarations-into-parameters.js.map +1 -1
- package/dist/transforms/pack.d.ts +4 -11
- package/dist/transforms/pack.d.ts.map +1 -1
- package/dist/transforms/pack.js +4 -17
- package/dist/transforms/pack.js.map +1 -1
- package/dist/transforms/remove-anonymous-function-names.d.ts +2 -4
- package/dist/transforms/remove-anonymous-function-names.d.ts.map +1 -1
- package/dist/transforms/remove-anonymous-function-names.js +2 -40
- package/dist/transforms/remove-anonymous-function-names.js.map +1 -1
- package/dist/transforms/remove-unreachable-code.js +2 -21
- package/dist/transforms/remove-unreachable-code.js.map +1 -1
- package/dist/transforms/remove-unused-code.d.ts.map +1 -1
- package/dist/transforms/remove-unused-code.js +5 -10
- package/dist/transforms/remove-unused-code.js.map +1 -1
- package/dist/transforms/rename-identifiers.d.ts +3 -3
- package/dist/transforms/rename-identifiers.d.ts.map +1 -1
- package/dist/transforms/rename-identifiers.js +4 -20
- package/dist/transforms/rename-identifiers.js.map +1 -1
- package/dist/transforms/reorder-function-declarations.d.ts +1 -2
- package/dist/transforms/reorder-function-declarations.d.ts.map +1 -1
- package/dist/transforms/reorder-function-declarations.js +2 -4
- package/dist/transforms/reorder-function-declarations.js.map +1 -1
- package/dist/transforms/specials-to-strings.d.ts.map +1 -1
- package/dist/transforms/specials-to-strings.js +2 -4
- package/dist/transforms/specials-to-strings.js.map +1 -1
- package/dist/transforms/switch-to-if.js +5 -15
- package/dist/transforms/switch-to-if.js.map +1 -1
- package/dist/transforms/ternary-to-if.d.ts.map +1 -1
- package/dist/transforms/ternary-to-if.js +5 -9
- package/dist/transforms/ternary-to-if.js.map +1 -1
- package/dist/transforms/update-to-assignment.d.ts.map +1 -1
- package/dist/transforms/update-to-assignment.js +6 -8
- package/dist/transforms/update-to-assignment.js.map +1 -1
- package/dist/transforms/yodify-conditions.d.ts +3 -4
- package/dist/transforms/yodify-conditions.d.ts.map +1 -1
- package/dist/transforms/yodify-conditions.js +3 -4
- package/dist/transforms/yodify-conditions.js.map +1 -1
- package/dist/utils/ast.d.ts +0 -1
- package/dist/utils/ast.d.ts.map +1 -1
- package/dist/utils/ast.js +0 -26
- package/dist/utils/ast.js.map +1 -1
- package/dist/utils/evaluation-order.js +1 -1
- package/dist/utils/evaluation-order.js.map +1 -1
- package/dist/utils/logical-lowering.d.ts.map +1 -1
- package/dist/utils/logical-lowering.js +7 -9
- package/dist/utils/logical-lowering.js.map +1 -1
- package/dist/utils/nullish.d.ts +4 -0
- package/dist/utils/nullish.d.ts.map +1 -0
- package/dist/utils/nullish.js +8 -0
- package/dist/utils/nullish.js.map +1 -0
- package/dist/utils/paths.d.ts +0 -2
- package/dist/utils/paths.d.ts.map +1 -1
- package/dist/utils/paths.js +0 -28
- package/dist/utils/paths.js.map +1 -1
- package/package.json +3 -13
- package/src/analysis/constant.ts +12 -30
- package/src/analysis/inlineability.ts +4 -12
- package/src/analysis/purity.ts +29 -92
- package/src/preparation/expand-destructuring.ts +2 -10
- package/src/transforms/arguments-to-parameters.ts +5 -4
- package/src/transforms/arrow-to-function.ts +6 -8
- package/src/transforms/collapse-single-use-temps.ts +12 -23
- package/src/transforms/conceal-strings.ts +3 -8
- package/src/transforms/const-let-to-var.ts +9 -100
- package/src/transforms/const-to-let.ts +1 -6
- package/src/transforms/drop-console.ts +3 -5
- package/src/transforms/expand-binary-assignment.ts +0 -5
- package/src/transforms/expand-logical-assignment.ts +0 -5
- package/src/transforms/extract-object-properties.ts +5 -14
- package/src/transforms/fold-builtin-methods.ts +3 -12
- package/src/transforms/fold-constants.ts +2 -2
- package/src/transforms/function-declaration-to-expression.ts +5 -6
- package/src/transforms/hoist-variables.ts +2 -12
- package/src/transforms/inline-functions.ts +16 -46
- package/src/transforms/logical-to-ternary.ts +5 -10
- package/src/transforms/mangle-properties.ts +2 -5
- package/src/transforms/nullish-coalescing-to-ternary.ts +7 -13
- package/src/transforms/object-method-to-property.ts +1 -1
- package/src/transforms/optional-chaining-to-ternary.ts +9 -9
- package/src/transforms/pack-declarations-into-parameters.ts +10 -16
- package/src/transforms/pack.ts +4 -17
- package/src/transforms/remove-anonymous-function-names.ts +3 -46
- package/src/transforms/remove-unreachable-code.ts +2 -22
- package/src/transforms/remove-unused-code.ts +5 -10
- package/src/transforms/rename-identifiers.ts +4 -20
- package/src/transforms/reorder-function-declarations.ts +2 -4
- package/src/transforms/specials-to-strings.ts +2 -4
- package/src/transforms/switch-to-if.ts +5 -22
- package/src/transforms/ternary-to-if.ts +5 -9
- package/src/transforms/update-to-assignment.ts +6 -8
- package/src/transforms/yodify-conditions.ts +3 -4
- package/src/utils/ast.ts +0 -29
- package/src/utils/evaluation-order.ts +1 -1
- package/src/utils/logical-lowering.ts +7 -11
- package/src/utils/nullish.ts +23 -0
- package/src/utils/paths.ts +0 -32
package/src/analysis/purity.ts
CHANGED
|
@@ -1,18 +1,11 @@
|
|
|
1
|
-
import type {
|
|
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
|
|
14
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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,
|
|
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
|
|
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
|
|
81
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
211
|
-
isSideEffectFree(node.consequent, scope
|
|
212
|
-
isSideEffectFree(node.alternate, scope
|
|
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
|
|
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
|
-
//
|
|
227
|
-
|
|
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
|
-
|
|
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
|
-
*
|
|
22
|
-
*
|
|
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
|
|
12
|
-
* with simple parameter lists.
|
|
13
|
-
*
|
|
14
|
-
*
|
|
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
|
-
*
|
|
11
|
-
* that
|
|
12
|
-
*
|
|
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.
|
|
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
|
-
//
|
|
337
|
-
|
|
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
|
|
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
|
|
667
|
+
return isPure(rhs.node, rhs.scope)
|
|
679
668
|
}
|
|
680
|
-
return isPure(expr.node, expr.scope
|
|
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
|
-
//
|
|
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
|
|
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.
|
|
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
|
|
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()
|
|
373
|
+
if (owner.isProgram()) {
|
|
379
374
|
return owner
|
|
380
375
|
}
|
|
381
376
|
const body = owner.get('body') as NodePath
|
|
@@ -3,14 +3,11 @@ import type { NodePath, Scope, Visitor } from '@babel/traverse'
|
|
|
3
3
|
import * as t from '@babel/types'
|
|
4
4
|
import type { File } from '@babel/types'
|
|
5
5
|
|
|
6
|
-
import { initializerReaches } from '../analysis/document-order'
|
|
7
|
-
import { isInsideWith } from '../utils/ast'
|
|
8
|
-
import { hasConstantViolation } from '../utils/paths'
|
|
9
|
-
|
|
10
6
|
/**
|
|
11
|
-
* Converts
|
|
12
|
-
*
|
|
13
|
-
*
|
|
7
|
+
* Converts `let` and `const` declarations to `var` and renames bindings that
|
|
8
|
+
* would conflict after hoisting. It leaves loop declarations unchanged when
|
|
9
|
+
* `var` would lose a captured per-iteration binding or reuse the previous
|
|
10
|
+
* iteration's value.
|
|
14
11
|
*
|
|
15
12
|
* @example
|
|
16
13
|
* // ◀️ before
|
|
@@ -33,36 +30,12 @@ export function constLetToVar(ast: File): boolean {
|
|
|
33
30
|
if (kind !== 'let' && kind !== 'const') {
|
|
34
31
|
return
|
|
35
32
|
}
|
|
36
|
-
// Babel's scope table cannot account for properties supplied by `with`.
|
|
37
|
-
if (isInsideWith(path)) {
|
|
38
|
-
return
|
|
39
|
-
}
|
|
40
|
-
// A `var` write would lose the `const` TypeError.
|
|
41
|
-
if (kind === 'const' && hasConstantViolation(path)) {
|
|
42
|
-
return
|
|
43
|
-
}
|
|
44
|
-
// Hoisted `var` reads `undefined` where a lexical binding throws in TDZ.
|
|
45
|
-
if (hasTdzRiskyReference(path)) {
|
|
46
|
-
return
|
|
47
|
-
}
|
|
48
33
|
// An uninitialized lexical loop head resets on each entry. `var` does not.
|
|
49
34
|
if (forHeadMissingInitializer(path)) {
|
|
50
35
|
return
|
|
51
36
|
}
|
|
52
37
|
const blockScope = path.scope
|
|
53
38
|
const varScope = blockScope.getFunctionParent() ?? blockScope.getProgramParent()
|
|
54
|
-
if (varScope.path.isProgram()) {
|
|
55
|
-
// Script `var` creates a global-object property; module `var` does not.
|
|
56
|
-
if (varScope.path.node.sourceType !== 'module') {
|
|
57
|
-
return
|
|
58
|
-
}
|
|
59
|
-
// A circular importer can read this binding before initialization. A
|
|
60
|
-
// lexical binding throws in TDZ then, while `var` reads `undefined`. That
|
|
61
|
-
// external read does not appear in this file's references.
|
|
62
|
-
if (bindsExportedName(path)) {
|
|
63
|
-
return
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
39
|
// One `var` cannot preserve per-iteration bindings captured by closures.
|
|
67
40
|
if (capturedInEnclosingLoop(path)) {
|
|
68
41
|
return
|
|
@@ -76,10 +49,6 @@ export function constLetToVar(ast: File): boolean {
|
|
|
76
49
|
if (!id) {
|
|
77
50
|
return
|
|
78
51
|
}
|
|
79
|
-
if (isInsideWith(path)) {
|
|
80
|
-
return
|
|
81
|
-
}
|
|
82
|
-
|
|
83
52
|
const scope = path.parentPath.scope
|
|
84
53
|
if (!isVarScope(scope) && scope.parent!.hasBinding(id.name, { noUids: true })) {
|
|
85
54
|
path.scope.rename(id.name)
|
|
@@ -107,35 +76,6 @@ const conflictingFunctionsVisitor: Visitor<{ names: string[] }> = {
|
|
|
107
76
|
},
|
|
108
77
|
}
|
|
109
78
|
|
|
110
|
-
// Same-scope uses need a reachable initializer. Nested uses also need their
|
|
111
|
-
// function value created after initialization.
|
|
112
|
-
function hasTdzRiskyReference(path: NodePath<t.VariableDeclaration>): boolean {
|
|
113
|
-
const declHome = nearestFunctionOrProgram(path)
|
|
114
|
-
for (const name of Object.keys(path.getBindingIdentifiers())) {
|
|
115
|
-
const binding = path.scope.getBinding(name)
|
|
116
|
-
if (!binding) {
|
|
117
|
-
continue
|
|
118
|
-
}
|
|
119
|
-
for (const usage of [...binding.referencePaths, ...binding.constantViolations]) {
|
|
120
|
-
if (usage.findParent((ancestor) => ancestor === path)) {
|
|
121
|
-
return true
|
|
122
|
-
}
|
|
123
|
-
const usageHome = nearestFunctionOrProgram(usage)
|
|
124
|
-
if (usageHome === declHome) {
|
|
125
|
-
if (!initializerReaches(path, usage)) {
|
|
126
|
-
return true
|
|
127
|
-
}
|
|
128
|
-
continue
|
|
129
|
-
}
|
|
130
|
-
const nested = outermostNestedFunction(usage, declHome)
|
|
131
|
-
if (!nested || nested.isFunctionDeclaration() || !initializerReaches(path, nested)) {
|
|
132
|
-
return true
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
return false
|
|
137
|
-
}
|
|
138
|
-
|
|
139
79
|
function forHeadMissingInitializer(path: NodePath<t.VariableDeclaration>): boolean {
|
|
140
80
|
if (!t.isForStatement(path.parent) || path.key !== 'init') {
|
|
141
81
|
return false
|
|
@@ -143,26 +83,6 @@ function forHeadMissingInitializer(path: NodePath<t.VariableDeclaration>): boole
|
|
|
143
83
|
return path.node.declarations.some((decl) => decl.init == null)
|
|
144
84
|
}
|
|
145
85
|
|
|
146
|
-
// A source clause re-exports another module's binding, not this local binding.
|
|
147
|
-
function bindsExportedName(path: NodePath<t.VariableDeclaration>): boolean {
|
|
148
|
-
if (path.parentPath.isExportNamedDeclaration()) {
|
|
149
|
-
return true
|
|
150
|
-
}
|
|
151
|
-
const names = new Set(Object.keys(path.getBindingIdentifiers()))
|
|
152
|
-
const program = path.scope.getProgramParent().path.node as t.Program
|
|
153
|
-
for (const statement of program.body) {
|
|
154
|
-
if (!t.isExportNamedDeclaration(statement) || statement.source) {
|
|
155
|
-
continue
|
|
156
|
-
}
|
|
157
|
-
for (const specifier of statement.specifiers) {
|
|
158
|
-
if (t.isExportSpecifier(specifier) && names.has(specifier.local.name)) {
|
|
159
|
-
return true
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
return false
|
|
164
|
-
}
|
|
165
|
-
|
|
166
86
|
// A nested function or class can observe the loop's per-iteration binding.
|
|
167
87
|
function capturedInEnclosingLoop(path: NodePath<t.VariableDeclaration>): boolean {
|
|
168
88
|
const loop = enclosingLoop(path)
|
|
@@ -174,7 +94,11 @@ function capturedInEnclosingLoop(path: NodePath<t.VariableDeclaration>): boolean
|
|
|
174
94
|
if (!binding) {
|
|
175
95
|
continue
|
|
176
96
|
}
|
|
177
|
-
|
|
97
|
+
const usages =
|
|
98
|
+
binding.kind === 'const'
|
|
99
|
+
? binding.referencePaths
|
|
100
|
+
: [...binding.referencePaths, ...binding.constantViolations]
|
|
101
|
+
for (const usage of usages) {
|
|
178
102
|
for (
|
|
179
103
|
let current = usage.parentPath;
|
|
180
104
|
current && current !== loop;
|
|
@@ -232,21 +156,6 @@ function transformBlockScopedVariable(
|
|
|
232
156
|
})
|
|
233
157
|
}
|
|
234
158
|
|
|
235
|
-
function nearestFunctionOrProgram(path: NodePath): NodePath {
|
|
236
|
-
return path.findParent((ancestor) => ancestor.isFunction() || ancestor.isProgram())!
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
// The shallowest nested function decides when this use can run.
|
|
240
|
-
function outermostNestedFunction(usage: NodePath, home: NodePath): NodePath | null {
|
|
241
|
-
let result: NodePath | null = null
|
|
242
|
-
for (let current = usage.parentPath; current && current !== home; current = current.parentPath) {
|
|
243
|
-
if (current.isFunction()) {
|
|
244
|
-
result = current
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
return result
|
|
248
|
-
}
|
|
249
|
-
|
|
250
159
|
function enclosingLoop(path: NodePath): NodePath<t.Loop> | null {
|
|
251
160
|
for (let current = path.parentPath; current; current = current.parentPath) {
|
|
252
161
|
if (current.isFunction()) {
|
|
@@ -3,11 +3,9 @@ import type { File } from '@babel/types'
|
|
|
3
3
|
|
|
4
4
|
import { traverseForChanges } from '../utils/change-tracking'
|
|
5
5
|
import type { ChangeState } from '../utils/change-tracking'
|
|
6
|
-
import { hasConstantViolation } from '../utils/paths'
|
|
7
6
|
|
|
8
7
|
/**
|
|
9
|
-
* Changes `const` to `let
|
|
10
|
-
* the same scope and TDZ behavior; the declarations differ only on writes.
|
|
8
|
+
* Changes `const` declarations to `let`.
|
|
11
9
|
*
|
|
12
10
|
* @example
|
|
13
11
|
* // ◀️ before
|
|
@@ -27,9 +25,6 @@ const visitor: Visitor<ChangeState> = {
|
|
|
27
25
|
if (path.node.kind !== 'const') {
|
|
28
26
|
return
|
|
29
27
|
}
|
|
30
|
-
if (hasConstantViolation(path)) {
|
|
31
|
-
return
|
|
32
|
-
}
|
|
33
28
|
path.node.kind = 'let'
|
|
34
29
|
state.changed = true
|
|
35
30
|
},
|