webfuscator 1.1.0 → 1.2.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 (155) hide show
  1. package/README.md +17 -12
  2. package/dist/analysis/constant.js +3 -3
  3. package/dist/analysis/constant.js.map +1 -1
  4. package/dist/analysis/inlineability.d.ts.map +1 -1
  5. package/dist/analysis/inlineability.js +39 -27
  6. package/dist/analysis/inlineability.js.map +1 -1
  7. package/dist/analysis/purity.js +1 -1
  8. package/dist/analysis/purity.js.map +1 -1
  9. package/dist/index.d.ts +2 -1
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js.map +1 -1
  12. package/dist/obfuscator.d.ts.map +1 -1
  13. package/dist/obfuscator.js.map +1 -1
  14. package/dist/options.d.ts +7 -0
  15. package/dist/options.d.ts.map +1 -1
  16. package/dist/preparation/expand-destructuring.js +4 -3
  17. package/dist/preparation/expand-destructuring.js.map +1 -1
  18. package/dist/preparation/expand-template-literals.js +3 -2
  19. package/dist/preparation/expand-template-literals.js.map +1 -1
  20. package/dist/preparation/split-sequence-expressions.js +1 -1
  21. package/dist/preparation/split-sequence-expressions.js.map +1 -1
  22. package/dist/preparation/split-variable-declarations.js +1 -1
  23. package/dist/preparation/split-variable-declarations.js.map +1 -1
  24. package/dist/preparation/stringify-object-keys.js +1 -1
  25. package/dist/preparation/stringify-object-keys.js.map +1 -1
  26. package/dist/preparation/wrap-single-statements.js +2 -2
  27. package/dist/preparation/wrap-single-statements.js.map +1 -1
  28. package/dist/transforms/arguments-to-parameters.js +1 -1
  29. package/dist/transforms/arguments-to-parameters.js.map +1 -1
  30. package/dist/transforms/arrow-to-function.js.map +1 -1
  31. package/dist/transforms/collapse-single-use-temps.js +7 -7
  32. package/dist/transforms/collapse-single-use-temps.js.map +1 -1
  33. package/dist/transforms/const-let-to-var.js +3 -3
  34. package/dist/transforms/const-let-to-var.js.map +1 -1
  35. package/dist/transforms/const-to-let.js.map +1 -1
  36. package/dist/transforms/do-while-to-while.js +3 -3
  37. package/dist/transforms/do-while-to-while.js.map +1 -1
  38. package/dist/transforms/drop-console.js +4 -3
  39. package/dist/transforms/drop-console.js.map +1 -1
  40. package/dist/transforms/drop-debugger.js.map +1 -1
  41. package/dist/transforms/expand-binary-assignment.js +5 -3
  42. package/dist/transforms/expand-binary-assignment.js.map +1 -1
  43. package/dist/transforms/expand-logical-assignment.js +5 -3
  44. package/dist/transforms/expand-logical-assignment.js.map +1 -1
  45. package/dist/transforms/extract-object-properties.js +7 -7
  46. package/dist/transforms/extract-object-properties.js.map +1 -1
  47. package/dist/transforms/fold-builtin-methods.js +16 -15
  48. package/dist/transforms/fold-builtin-methods.js.map +1 -1
  49. package/dist/transforms/fold-constants.js +2 -2
  50. package/dist/transforms/fold-constants.js.map +1 -1
  51. package/dist/transforms/for-to-while.js +5 -5
  52. package/dist/transforms/for-to-while.js.map +1 -1
  53. package/dist/transforms/function-declaration-to-expression.js +3 -3
  54. package/dist/transforms/function-declaration-to-expression.js.map +1 -1
  55. package/dist/transforms/inline-functions.js +7 -7
  56. package/dist/transforms/inline-functions.js.map +1 -1
  57. package/dist/transforms/logical-to-ternary.js +2 -2
  58. package/dist/transforms/logical-to-ternary.js.map +1 -1
  59. package/dist/transforms/mangle-properties.d.ts.map +1 -1
  60. package/dist/transforms/mangle-properties.js +35 -86
  61. package/dist/transforms/mangle-properties.js.map +1 -1
  62. package/dist/transforms/nullish-coalescing-to-ternary.js +2 -2
  63. package/dist/transforms/nullish-coalescing-to-ternary.js.map +1 -1
  64. package/dist/transforms/numbers-to-strings.js +2 -2
  65. package/dist/transforms/numbers-to-strings.js.map +1 -1
  66. package/dist/transforms/object-method-to-property.js +1 -1
  67. package/dist/transforms/object-method-to-property.js.map +1 -1
  68. package/dist/transforms/optional-chaining-to-ternary.js +2 -2
  69. package/dist/transforms/optional-chaining-to-ternary.js.map +1 -1
  70. package/dist/transforms/pack-declarations-into-parameters.js +2 -2
  71. package/dist/transforms/pack-declarations-into-parameters.js.map +1 -1
  72. package/dist/transforms/pack.d.ts.map +1 -1
  73. package/dist/transforms/pack.js +194 -26
  74. package/dist/transforms/pack.js.map +1 -1
  75. package/dist/transforms/remove-anonymous-function-names.js +3 -3
  76. package/dist/transforms/remove-anonymous-function-names.js.map +1 -1
  77. package/dist/transforms/remove-unreachable-code.js +1 -1
  78. package/dist/transforms/remove-unreachable-code.js.map +1 -1
  79. package/dist/transforms/remove-unused-code.js +4 -4
  80. package/dist/transforms/remove-unused-code.js.map +1 -1
  81. package/dist/transforms/rename-identifiers.d.ts.map +1 -1
  82. package/dist/transforms/rename-identifiers.js +4 -3
  83. package/dist/transforms/rename-identifiers.js.map +1 -1
  84. package/dist/transforms/specials-to-strings.d.ts.map +1 -1
  85. package/dist/transforms/specials-to-strings.js +2 -2
  86. package/dist/transforms/specials-to-strings.js.map +1 -1
  87. package/dist/transforms/switch-to-if.js +7 -7
  88. package/dist/transforms/switch-to-if.js.map +1 -1
  89. package/dist/transforms/ternary-to-if.js +7 -4
  90. package/dist/transforms/ternary-to-if.js.map +1 -1
  91. package/dist/transforms/update-to-assignment.js +4 -2
  92. package/dist/transforms/update-to-assignment.js.map +1 -1
  93. package/dist/transforms/yodify-conditions.js +3 -3
  94. package/dist/transforms/yodify-conditions.js.map +1 -1
  95. package/dist/utils/ast.d.ts.map +1 -1
  96. package/dist/utils/ast.js +2 -1
  97. package/dist/utils/ast.js.map +1 -1
  98. package/dist/utils/evaluation-order.js.map +1 -1
  99. package/dist/utils/logical-lowering.js +2 -2
  100. package/dist/utils/logical-lowering.js.map +1 -1
  101. package/dist/utils/loop-lowering.js +1 -1
  102. package/dist/utils/loop-lowering.js.map +1 -1
  103. package/dist/utils/string-generator.js +1 -1
  104. package/dist/utils/string-generator.js.map +1 -1
  105. package/package.json +14 -12
  106. package/src/analysis/constant.ts +4 -4
  107. package/src/analysis/inlineability.ts +43 -35
  108. package/src/analysis/purity.ts +3 -3
  109. package/src/index.ts +4 -2
  110. package/src/obfuscator.ts +42 -42
  111. package/src/options.ts +8 -1
  112. package/src/preparation/expand-destructuring.ts +5 -4
  113. package/src/preparation/expand-template-literals.ts +4 -3
  114. package/src/preparation/split-sequence-expressions.ts +2 -2
  115. package/src/preparation/split-variable-declarations.ts +1 -1
  116. package/src/preparation/stringify-object-keys.ts +2 -2
  117. package/src/preparation/wrap-single-statements.ts +2 -2
  118. package/src/transforms/arguments-to-parameters.ts +5 -5
  119. package/src/transforms/arrow-to-function.ts +3 -3
  120. package/src/transforms/collapse-single-use-temps.ts +14 -14
  121. package/src/transforms/const-let-to-var.ts +6 -6
  122. package/src/transforms/const-to-let.ts +4 -4
  123. package/src/transforms/do-while-to-while.ts +6 -6
  124. package/src/transforms/drop-console.ts +8 -7
  125. package/src/transforms/drop-debugger.ts +2 -2
  126. package/src/transforms/expand-binary-assignment.ts +8 -6
  127. package/src/transforms/expand-logical-assignment.ts +8 -6
  128. package/src/transforms/extract-object-properties.ts +12 -12
  129. package/src/transforms/fold-builtin-methods.ts +20 -19
  130. package/src/transforms/fold-constants.ts +8 -8
  131. package/src/transforms/for-to-while.ts +8 -8
  132. package/src/transforms/function-declaration-to-expression.ts +5 -5
  133. package/src/transforms/inline-functions.ts +14 -14
  134. package/src/transforms/logical-to-ternary.ts +7 -7
  135. package/src/transforms/mangle-properties.ts +43 -97
  136. package/src/transforms/nullish-coalescing-to-ternary.ts +6 -6
  137. package/src/transforms/numbers-to-strings.ts +4 -4
  138. package/src/transforms/object-method-to-property.ts +4 -4
  139. package/src/transforms/optional-chaining-to-ternary.ts +5 -5
  140. package/src/transforms/pack-declarations-into-parameters.ts +7 -7
  141. package/src/transforms/pack.ts +212 -33
  142. package/src/transforms/remove-anonymous-function-names.ts +5 -5
  143. package/src/transforms/remove-unreachable-code.ts +3 -3
  144. package/src/transforms/remove-unused-code.ts +8 -8
  145. package/src/transforms/rename-identifiers.ts +9 -8
  146. package/src/transforms/specials-to-strings.ts +7 -7
  147. package/src/transforms/switch-to-if.ts +12 -12
  148. package/src/transforms/ternary-to-if.ts +12 -9
  149. package/src/transforms/update-to-assignment.ts +7 -5
  150. package/src/transforms/yodify-conditions.ts +6 -6
  151. package/src/utils/ast.ts +2 -1
  152. package/src/utils/evaluation-order.ts +1 -1
  153. package/src/utils/logical-lowering.ts +3 -3
  154. package/src/utils/loop-lowering.ts +1 -1
  155. package/src/utils/string-generator.ts +1 -1
@@ -1,13 +1,13 @@
1
1
  import type { Binding, NodePath, Scope } from '@babel/traverse'
2
2
  import * as t from '@babel/types'
3
3
 
4
+ import { enclosingScopeHasDirectEval, isInsideWith } from '../utils/ast'
4
5
  import {
5
6
  declarationReaches,
6
7
  initializerReaches,
7
8
  readCrossesFunctionBoundary,
8
- } from 'src/analysis/document-order'
9
- import { isSideEffectFree } from 'src/analysis/purity'
10
- import { enclosingScopeHasDirectEval, isInsideWith } from 'src/utils/ast'
9
+ } from './document-order'
10
+ import { isSideEffectFree } from './purity'
11
11
 
12
12
  export type Evaluation = { known: true; value: unknown } | { known: false }
13
13
 
@@ -48,7 +48,7 @@ const EVALUATORS: Partial<Record<t.Node['type'], Evaluator>> = {
48
48
  if (template.expressions.length > 0) {
49
49
  return UNKNOWN
50
50
  }
51
- const [quasi] = template.quasis
51
+ const quasi = template.quasis[0]
52
52
  return { known: true, value: quasi?.value.cooked ?? quasi?.value.raw ?? '' }
53
53
  },
54
54
  UnaryExpression: evaluateUnary,
@@ -3,8 +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 { walkOwnFunctionScope } from 'src/utils/ast'
7
- import { hasAnnexBFunctionAlias, referencesOrWritesVariable } from 'src/utils/paths'
6
+ import { hasAnnexBFunctionAlias, referencesOrWritesVariable } from '../utils/paths'
8
7
 
9
8
  export interface InlineCandidate {
10
9
  name: string
@@ -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
 
@@ -47,7 +49,7 @@ export function analyzeInlineability(ast: File): Map<string, InlineCandidate> {
47
49
  if (!t.isIdentifier(path.node.id)) {
48
50
  return
49
51
  }
50
- const { init } = path.node
52
+ const init = path.node.init
51
53
  if (!t.isFunctionExpression(init)) {
52
54
  return
53
55
  }
@@ -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
@@ -267,11 +278,8 @@ const analyzeBodyVisitor: Visitor<AnalyzeBodyState> = {
267
278
  path.stop()
268
279
  },
269
280
  Identifier(path, state) {
270
- const { name } = path.node
271
- if (
272
- state.innerFnDepth === 0 &&
273
- (name === 'arguments' || name === 'eval' || name === state.candidateName)
274
- ) {
281
+ const name = path.node.name
282
+ if (state.innerFnDepth === 0 && (name === 'arguments' || name === 'eval')) {
275
283
  state.unsafe = true
276
284
  path.stop()
277
285
  return
@@ -1,13 +1,13 @@
1
1
  import type { Binding, NodePath, Scope } from '@babel/traverse'
2
2
  import * as t from '@babel/types'
3
3
 
4
+ import { isInsideWith } from '../utils/ast'
5
+ import { isPrimitiveLiteral } from '../utils/literal'
4
6
  import {
5
7
  declarationReaches,
6
8
  initializerReaches,
7
9
  readCrossesFunctionBoundary,
8
- } from 'src/analysis/document-order'
9
- import { isInsideWith } from 'src/utils/ast'
10
- import { isPrimitiveLiteral } from 'src/utils/literal'
10
+ } from './document-order'
11
11
 
12
12
  // Purity also requires deterministic repetition and no throw. Identifier reads
13
13
  // need scope and reference paths to prove resolution, initialization, and the
package/src/index.ts CHANGED
@@ -1,7 +1,9 @@
1
- export { obfuscate } from 'src/obfuscator'
1
+ export { obfuscate } from './obfuscator'
2
2
  export type {
3
3
  ManglePropertiesOptions,
4
4
  ObfuscatorOptions,
5
5
  PackOptions,
6
6
  PropertyNameGenerator,
7
- } from 'src/options'
7
+ TransformName,
8
+ } from './options'
9
+ export type { StringGeneratorMode, StringGeneratorModeOption } from './utils/string-generator'
package/src/obfuscator.ts CHANGED
@@ -3,48 +3,48 @@ import { parse } from '@babel/parser'
3
3
  import type { ParserOptions } from '@babel/parser'
4
4
  import type { File } from '@babel/types'
5
5
 
6
- import type { ObfuscatorOptions, TransformContext, TransformName } from 'src/options'
7
- import { dotToBracket } from 'src/preparation/dot-to-bracket'
8
- import { expandDestructuring } from 'src/preparation/expand-destructuring'
9
- import { expandTemplateLiterals } from 'src/preparation/expand-template-literals'
10
- import { splitSequenceExpressions } from 'src/preparation/split-sequence-expressions'
11
- import { splitVariableDeclarations } from 'src/preparation/split-variable-declarations'
12
- import { stringifyObjectKeys } from 'src/preparation/stringify-object-keys'
13
- import { wrapSingleStatements } from 'src/preparation/wrap-single-statements'
14
- import { argumentsToParameters } from 'src/transforms/arguments-to-parameters'
15
- import { arrowToFunction } from 'src/transforms/arrow-to-function'
16
- import { collapseSingleUseTemps } from 'src/transforms/collapse-single-use-temps'
17
- import { constLetToVar } from 'src/transforms/const-let-to-var'
18
- import { constToLet } from 'src/transforms/const-to-let'
19
- import { doWhileToWhile } from 'src/transforms/do-while-to-while'
20
- import { dropConsole } from 'src/transforms/drop-console'
21
- import { dropDebugger } from 'src/transforms/drop-debugger'
22
- import { expandBinaryAssignment } from 'src/transforms/expand-binary-assignment'
23
- import { expandLogicalAssignment } from 'src/transforms/expand-logical-assignment'
24
- import { extractObjectProperties } from 'src/transforms/extract-object-properties'
25
- import { foldBuiltinMethods } from 'src/transforms/fold-builtin-methods'
26
- import { foldConstants } from 'src/transforms/fold-constants'
27
- import { forToWhile } from 'src/transforms/for-to-while'
28
- import { functionDeclarationToExpression } from 'src/transforms/function-declaration-to-expression'
29
- import { inlineFunctions } from 'src/transforms/inline-functions'
30
- import { logicalToTernary } from 'src/transforms/logical-to-ternary'
31
- import { mangleProperties } from 'src/transforms/mangle-properties'
32
- import { nullishCoalescingToTernary } from 'src/transforms/nullish-coalescing-to-ternary'
33
- import { numbersToStrings } from 'src/transforms/numbers-to-strings'
34
- import { objectMethodToProperty } from 'src/transforms/object-method-to-property'
35
- import { optionalChainingToTernary } from 'src/transforms/optional-chaining-to-ternary'
36
- import { pack } from 'src/transforms/pack'
37
- import { packDeclarationsIntoParameters } from 'src/transforms/pack-declarations-into-parameters'
38
- import { removeAnonymousFunctionNames } from 'src/transforms/remove-anonymous-function-names'
39
- import { removeUnreachableCode } from 'src/transforms/remove-unreachable-code'
40
- import { removeUnusedCode } from 'src/transforms/remove-unused-code'
41
- import { renameIdentifiers } from 'src/transforms/rename-identifiers'
42
- import { specialsToStrings } from 'src/transforms/specials-to-strings'
43
- import { switchToIf } from 'src/transforms/switch-to-if'
44
- import { ternaryToIf } from 'src/transforms/ternary-to-if'
45
- import { updateToAssignment } from 'src/transforms/update-to-assignment'
46
- import { yodifyConditions } from 'src/transforms/yodify-conditions'
47
- import type { StringGeneratorModeOption } from 'src/utils/string-generator'
6
+ import type { ObfuscatorOptions, TransformContext, TransformName } from './options'
7
+ import { dotToBracket } from './preparation/dot-to-bracket'
8
+ import { expandDestructuring } from './preparation/expand-destructuring'
9
+ import { expandTemplateLiterals } from './preparation/expand-template-literals'
10
+ import { splitSequenceExpressions } from './preparation/split-sequence-expressions'
11
+ import { splitVariableDeclarations } from './preparation/split-variable-declarations'
12
+ import { stringifyObjectKeys } from './preparation/stringify-object-keys'
13
+ import { wrapSingleStatements } from './preparation/wrap-single-statements'
14
+ import { argumentsToParameters } from './transforms/arguments-to-parameters'
15
+ import { arrowToFunction } from './transforms/arrow-to-function'
16
+ import { collapseSingleUseTemps } from './transforms/collapse-single-use-temps'
17
+ import { constLetToVar } from './transforms/const-let-to-var'
18
+ import { constToLet } from './transforms/const-to-let'
19
+ import { doWhileToWhile } from './transforms/do-while-to-while'
20
+ import { dropConsole } from './transforms/drop-console'
21
+ import { dropDebugger } from './transforms/drop-debugger'
22
+ import { expandBinaryAssignment } from './transforms/expand-binary-assignment'
23
+ import { expandLogicalAssignment } from './transforms/expand-logical-assignment'
24
+ import { extractObjectProperties } from './transforms/extract-object-properties'
25
+ import { foldBuiltinMethods } from './transforms/fold-builtin-methods'
26
+ import { foldConstants } from './transforms/fold-constants'
27
+ import { forToWhile } from './transforms/for-to-while'
28
+ import { functionDeclarationToExpression } from './transforms/function-declaration-to-expression'
29
+ import { inlineFunctions } from './transforms/inline-functions'
30
+ import { logicalToTernary } from './transforms/logical-to-ternary'
31
+ import { mangleProperties } from './transforms/mangle-properties'
32
+ import { nullishCoalescingToTernary } from './transforms/nullish-coalescing-to-ternary'
33
+ import { numbersToStrings } from './transforms/numbers-to-strings'
34
+ import { objectMethodToProperty } from './transforms/object-method-to-property'
35
+ import { optionalChainingToTernary } from './transforms/optional-chaining-to-ternary'
36
+ import { pack } from './transforms/pack'
37
+ import { packDeclarationsIntoParameters } from './transforms/pack-declarations-into-parameters'
38
+ import { removeAnonymousFunctionNames } from './transforms/remove-anonymous-function-names'
39
+ import { removeUnreachableCode } from './transforms/remove-unreachable-code'
40
+ import { removeUnusedCode } from './transforms/remove-unused-code'
41
+ import { renameIdentifiers } from './transforms/rename-identifiers'
42
+ import { specialsToStrings } from './transforms/specials-to-strings'
43
+ import { switchToIf } from './transforms/switch-to-if'
44
+ import { ternaryToIf } from './transforms/ternary-to-if'
45
+ import { updateToAssignment } from './transforms/update-to-assignment'
46
+ import { yodifyConditions } from './transforms/yodify-conditions'
47
+ import type { StringGeneratorModeOption } from './utils/string-generator'
48
48
 
49
49
  const parserOptions: ParserOptions = { sourceType: 'unambiguous' }
50
50
 
package/src/options.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { StringGeneratorModeOption } from 'src/utils/string-generator'
1
+ import type { StringGeneratorModeOption } from './utils/string-generator'
2
2
 
3
3
  /**
4
4
  * Exported transform entry points. Preparation passes are always enabled and
@@ -84,6 +84,13 @@ export interface PackOptions {
84
84
  * source is strict. This deliberately changes behavior. Default `false`.
85
85
  */
86
86
  escapeStrict?: boolean
87
+ /**
88
+ * Free names to leave un-routed because they resolve to the real global from
89
+ * inside a detached Function body. `true` uses the built-in standard-globals
90
+ * list; an array names your own set. Stripped imports always route regardless,
91
+ * since a Function body cannot reach them. Default routes every free name.
92
+ */
93
+ skipGlobals?: boolean | readonly string[]
87
94
  /** Override the default string-generator mode for the names pack creates. */
88
95
  stringGeneratorMode?: StringGeneratorModeOption
89
96
  }
@@ -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 'src/utils/ast'
6
+ import { enclosingScopeHasDirectEval, isInsideWith } from '../utils/ast'
7
7
 
8
8
  type Kind = t.VariableDeclaration['kind']
9
9
 
@@ -47,12 +47,13 @@ const visitor = {
47
47
  return
48
48
  }
49
49
 
50
- const { kind } = path.node
50
+ const kind = path.node.kind
51
51
  const replacement: t.Statement[] = []
52
52
  let changed = false
53
53
 
54
54
  for (const declarator of path.node.declarations) {
55
- const { id, init } = declarator
55
+ const id = declarator.id
56
+ const init = declarator.init
56
57
 
57
58
  if (
58
59
  !init ||
@@ -135,7 +136,7 @@ function expandPattern(
135
136
  if (t.isRestElement(prop)) {
136
137
  continue
137
138
  }
138
- const { key } = prop
139
+ const key = prop.key
139
140
  const useComputed = prop.computed || !t.isIdentifier(key)
140
141
  const access = t.memberExpression(
141
142
  t.cloneNode(src),
@@ -3,7 +3,7 @@ import type { Visitor } from '@babel/traverse'
3
3
  import * as t from '@babel/types'
4
4
  import type { File } from '@babel/types'
5
5
 
6
- import { isInDirectivePrologue } from 'src/utils/ast'
6
+ import { isInDirectivePrologue } from '../utils/ast'
7
7
 
8
8
  /**
9
9
  * Rewrites safe template literals as string concatenation. Tagged templates
@@ -31,12 +31,13 @@ export function expandTemplateLiterals(ast: File): void {
31
31
 
32
32
  const visitor: Visitor = {
33
33
  TemplateLiteral(path) {
34
- const { parent } = path
34
+ const parent = path.parent
35
35
  if (t.isTaggedTemplateExpression(parent) && parent.quasi === path.node) {
36
36
  return
37
37
  }
38
38
 
39
- const { quasis, expressions } = path.node
39
+ const quasis = path.node.quasis
40
+ const expressions = path.node.expressions
40
41
 
41
42
  if (expressions.length === 0) {
42
43
  // Rewriting a leading template as a string would create a directive.
@@ -3,7 +3,7 @@ import type { NodePath } from '@babel/traverse'
3
3
  import * as t from '@babel/types'
4
4
  import type { File } from '@babel/types'
5
5
 
6
- import { isInDirectivePrologue } from 'src/utils/ast'
6
+ import { isInDirectivePrologue } from '../utils/ast'
7
7
 
8
8
  /**
9
9
  * Splits comma-sequenced expression statements into one statement each.
@@ -30,7 +30,7 @@ const visitor = {
30
30
  return
31
31
  }
32
32
  // Splitting a leading string out of a sequence could create a directive.
33
- const [first] = path.node.expression.expressions
33
+ const first = path.node.expression.expressions[0]
34
34
  if (t.isStringLiteral(first) && isInDirectivePrologue(path)) {
35
35
  return
36
36
  }
@@ -32,7 +32,7 @@ const visitor = {
32
32
  if (path.node.declarations.length <= 1) {
33
33
  return
34
34
  }
35
- const { kind } = path.node
35
+ const kind = path.node.kind
36
36
  const replacements = path.node.declarations.map((decl) => t.variableDeclaration(kind, [decl]))
37
37
  path.replaceWithMultiple(replacements)
38
38
  },
@@ -3,7 +3,7 @@ import type { NodePath } from '@babel/traverse'
3
3
  import * as t from '@babel/types'
4
4
  import type { File } from '@babel/types'
5
5
 
6
- import { isProtoKey } from 'src/utils/ast'
6
+ import { isProtoKey } from '../utils/ast'
7
7
 
8
8
  /**
9
9
  * Rewrites property keys in object literals and classes as computed strings.
@@ -57,7 +57,7 @@ function keyOrderIsSpreadSensitive(path: NodePath<t.ObjectProperty | t.ObjectMet
57
57
  if (!object || !object.isObjectExpression()) {
58
58
  return false
59
59
  }
60
- const { properties } = object.node
60
+ const properties = object.node.properties
61
61
  const hasSpread = properties.some((prop) => t.isSpreadElement(prop))
62
62
  if (!hasSpread) {
63
63
  return false
@@ -36,7 +36,7 @@ export function wrapSingleStatements(ast: File): void {
36
36
 
37
37
  const visitor = {
38
38
  IfStatement(path: { node: t.IfStatement }) {
39
- const { node } = path
39
+ const node = path.node
40
40
  if (!t.isBlockStatement(node.consequent)) {
41
41
  node.consequent = t.blockStatement([node.consequent])
42
42
  }
@@ -48,7 +48,7 @@ const visitor = {
48
48
  wrapLoopBody(path.node)
49
49
  },
50
50
  ArrowFunctionExpression(path: { node: t.ArrowFunctionExpression }) {
51
- const { node } = path
51
+ const node = path.node
52
52
  if (!t.isBlockStatement(node.body)) {
53
53
  node.body = t.blockStatement([t.returnStatement(node.body)])
54
54
  node.expression = false
@@ -2,10 +2,10 @@ 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 { hasDuplicateParamNames } from 'src/utils/ast'
6
- import { traverseForChanges } from 'src/utils/change-tracking'
7
- import type { ChangeState } from 'src/utils/change-tracking'
8
- import { isInStrictContext } from 'src/utils/paths'
5
+ import { hasDuplicateParamNames } from '../utils/ast'
6
+ import { traverseForChanges } from '../utils/change-tracking'
7
+ import type { ChangeState } from '../utils/change-tracking'
8
+ import { isInStrictContext } from '../utils/paths'
9
9
 
10
10
  /**
11
11
  * Replaces `arguments[i]` reads with their mapped parameter in sloppy functions
@@ -113,7 +113,7 @@ function processFunction(path: NodePath<t.Function>): boolean {
113
113
  if (path.isArrowFunctionExpression()) {
114
114
  return false
115
115
  }
116
- const { params } = path.node
116
+ const params = path.node.params
117
117
  // Rest, defaults, and destructuring make `arguments` unmapped.
118
118
  if (!params.every((param) => t.isIdentifier(param))) {
119
119
  return false
@@ -2,9 +2,9 @@ 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 'src/utils/ast'
6
- import { traverseForChanges } from 'src/utils/change-tracking'
7
- import type { ChangeState } from 'src/utils/change-tracking'
5
+ import { enclosingScopeHasDirectEval, isInsideWith } from '../utils/ast'
6
+ import { traverseForChanges } from '../utils/change-tracking'
7
+ import type { ChangeState } from '../utils/change-tracking'
8
8
 
9
9
  /**
10
10
  * Lowers arrows with Babel's capture-aware `arrowFunctionToExpression`. Arrows
@@ -3,17 +3,17 @@ import type { Binding, 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 { evaluateConstant } from 'src/analysis/constant'
6
+ import { evaluateConstant } from '../analysis/constant'
7
7
  import {
8
8
  declarationReaches,
9
9
  initializerReaches,
10
10
  readCrossesFunctionBoundary,
11
- } from 'src/analysis/document-order'
12
- import { isPure } from 'src/analysis/purity'
13
- import { isInsideWith } from 'src/utils/ast'
14
- import { reorderableToStatement } from 'src/utils/evaluation-order'
15
- import { valueToLiteral } from 'src/utils/literal'
16
- import { isCalleeOrTagOf } from 'src/utils/paths'
11
+ } from '../analysis/document-order'
12
+ import { isPure } from '../analysis/purity'
13
+ import { isInsideWith } from '../utils/ast'
14
+ import { reorderableToStatement } from '../utils/evaluation-order'
15
+ import { valueToLiteral } from '../utils/literal'
16
+ import { isCalleeOrTagOf } from '../utils/paths'
17
17
 
18
18
  /**
19
19
  * Drops single-use temporaries through substitution, direct branch writes, or
@@ -125,7 +125,7 @@ function trySubstituteSingleUse(
125
125
  binding: Binding,
126
126
  declaratorPath: NodePath<t.VariableDeclarator>,
127
127
  ): boolean {
128
- const [readPath] = binding.referencePaths
128
+ const readPath = binding.referencePaths[0]
129
129
  if (!readPath || !isPathLive(readPath)) {
130
130
  return false
131
131
  }
@@ -138,7 +138,7 @@ function trySubstituteSingleUse(
138
138
  return false
139
139
  }
140
140
 
141
- const { init } = declaratorPath.node
141
+ const init = declaratorPath.node.init
142
142
  let writeStmt: NodePath
143
143
  let writeValueOwner: NodePath
144
144
  let writeRhs: t.Expression
@@ -255,7 +255,7 @@ function tryRenameWritesToReader(
255
255
  return false
256
256
  }
257
257
 
258
- const [readPath] = binding.referencePaths
258
+ const readPath = binding.referencePaths[0]
259
259
  if (!readPath || !isPathLive(readPath)) {
260
260
  return false
261
261
  }
@@ -389,7 +389,7 @@ function tryScatterIntoTerminator(
389
389
  return false
390
390
  }
391
391
 
392
- const [readPath] = binding.referencePaths
392
+ const readPath = binding.referencePaths[0]
393
393
  if (!readPath || !isPathLive(readPath)) {
394
394
  return false
395
395
  }
@@ -648,7 +648,7 @@ function combineFall(a: boolean | null, b: boolean | null): boolean | null {
648
648
  // Apart from redirected writes, the span must run no user code or observations.
649
649
  // Redirected right sides must also be pure.
650
650
  function spanStatementIsInert(path: NodePath, tempBinding: Binding): boolean {
651
- const { node } = path
651
+ const node = path.node
652
652
  if (t.isEmptyStatement(node) || t.isBreakStatement(node) || t.isContinueStatement(node)) {
653
653
  return true
654
654
  }
@@ -715,7 +715,7 @@ function tryPropagateConstant(
715
715
  return false
716
716
  }
717
717
 
718
- const { init } = declaratorPath.node
718
+ const init = declaratorPath.node.init
719
719
  let writeRhs: t.Expression
720
720
  let writeRhsPath: NodePath
721
721
  let writeStmtToRemove: NodePath | null
@@ -886,7 +886,7 @@ function subtreeReadsBinding(stmtPath: NodePath, target: Binding): boolean {
886
886
  }
887
887
 
888
888
  function isStatementMovementSafe(path: NodePath): boolean {
889
- const { node } = path
889
+ const node = path.node
890
890
  if (t.isEmptyStatement(node)) {
891
891
  return true
892
892
  }
@@ -3,9 +3,9 @@ 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 'src/analysis/document-order'
7
- import { enclosingScopeHasDirectEval, isInsideWith } from 'src/utils/ast'
8
- import { hasConstantViolation } from 'src/utils/paths'
6
+ import { initializerReaches } from '../analysis/document-order'
7
+ import { enclosingScopeHasDirectEval, isInsideWith } from '../utils/ast'
8
+ import { hasConstantViolation } from '../utils/paths'
9
9
 
10
10
  /**
11
11
  * Lifts safe lexical declarations to function-scoped `var`, renaming collisions
@@ -29,7 +29,7 @@ export function constLetToVar(ast: File): boolean {
29
29
  const state = { changed: false }
30
30
  traverse(ast, {
31
31
  VariableDeclaration(path) {
32
- const { kind } = path.node
32
+ const kind = path.node.kind
33
33
  if (kind !== 'let' && kind !== 'const') {
34
34
  return
35
35
  }
@@ -64,7 +64,7 @@ export function constLetToVar(ast: File): boolean {
64
64
  },
65
65
 
66
66
  ClassDeclaration(path) {
67
- const { id } = path.node
67
+ const id = path.node.id
68
68
  if (!id) {
69
69
  return
70
70
  }
@@ -72,7 +72,7 @@ export function constLetToVar(ast: File): boolean {
72
72
  return
73
73
  }
74
74
 
75
- const { scope } = path.parentPath
75
+ const scope = path.parentPath.scope
76
76
  if (!isVarScope(scope) && scope.parent!.hasBinding(id.name, { noUids: true })) {
77
77
  path.scope.rename(id.name)
78
78
  state.changed = true
@@ -1,10 +1,10 @@
1
1
  import type { Visitor } from '@babel/traverse'
2
2
  import type { File } from '@babel/types'
3
3
 
4
- import { enclosingScopeHasDirectEval } from 'src/utils/ast'
5
- import { traverseForChanges } from 'src/utils/change-tracking'
6
- import type { ChangeState } from 'src/utils/change-tracking'
7
- import { hasConstantViolation } from 'src/utils/paths'
4
+ import { enclosingScopeHasDirectEval } from '../utils/ast'
5
+ import { traverseForChanges } from '../utils/change-tracking'
6
+ import type { ChangeState } from '../utils/change-tracking'
7
+ import { hasConstantViolation } from '../utils/paths'
8
8
 
9
9
  /**
10
10
  * Changes `const` to `let` when no write can observe the lost TypeError. Direct
@@ -2,9 +2,9 @@ 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 { traverseForChanges } from 'src/utils/change-tracking'
6
- import type { ChangeState } from 'src/utils/change-tracking'
7
- import { collectLoopLabels, generateLoopLabel, redirectContinues } from 'src/utils/loop-lowering'
5
+ import { traverseForChanges } from '../utils/change-tracking'
6
+ import type { ChangeState } from '../utils/change-tracking'
7
+ import { collectLoopLabels, generateLoopLabel, redirectContinues } from '../utils/loop-lowering'
8
8
 
9
9
  /**
10
10
  * Rewrites `do` loops as `while (true)` with an exit test after the body.
@@ -41,8 +41,8 @@ const visitor: Visitor<ChangeState> = {
41
41
  }
42
42
 
43
43
  function transformDoWhile(path: NodePath<t.DoWhileStatement>): void {
44
- const { node } = path
45
- const { ourLabels } = collectLoopLabels(path)
44
+ const node = path.node
45
+ const ourLabels = collectLoopLabels(path).ourLabels
46
46
  const innerLabel = generateLoopLabel(path, 'doIteration')
47
47
 
48
48
  const blockBody: t.BlockStatement = t.isBlockStatement(node.body)
@@ -58,7 +58,7 @@ function transformDoWhile(path: NodePath<t.DoWhileStatement>): void {
58
58
  t.blockStatement([labeledBlock, exitTest]),
59
59
  )
60
60
 
61
- const [newPath] = path.replaceWith(whileStmt)
61
+ const newPath = path.replaceWith(whileStmt)[0]
62
62
  const rewriteCount = redirectContinues(newPath, innerLabel, ourLabels)
63
63
 
64
64
  // Keep the block so body bindings cannot shadow names in the exit test.