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.
- package/README.md +17 -12
- package/dist/analysis/constant.js +3 -3
- package/dist/analysis/constant.js.map +1 -1
- package/dist/analysis/inlineability.d.ts.map +1 -1
- package/dist/analysis/inlineability.js +39 -27
- package/dist/analysis/inlineability.js.map +1 -1
- package/dist/analysis/purity.js +1 -1
- package/dist/analysis/purity.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/obfuscator.d.ts.map +1 -1
- package/dist/obfuscator.js.map +1 -1
- package/dist/options.d.ts +7 -0
- package/dist/options.d.ts.map +1 -1
- package/dist/preparation/expand-destructuring.js +4 -3
- package/dist/preparation/expand-destructuring.js.map +1 -1
- package/dist/preparation/expand-template-literals.js +3 -2
- package/dist/preparation/expand-template-literals.js.map +1 -1
- package/dist/preparation/split-sequence-expressions.js +1 -1
- package/dist/preparation/split-sequence-expressions.js.map +1 -1
- package/dist/preparation/split-variable-declarations.js +1 -1
- package/dist/preparation/split-variable-declarations.js.map +1 -1
- package/dist/preparation/stringify-object-keys.js +1 -1
- package/dist/preparation/stringify-object-keys.js.map +1 -1
- package/dist/preparation/wrap-single-statements.js +2 -2
- package/dist/preparation/wrap-single-statements.js.map +1 -1
- package/dist/transforms/arguments-to-parameters.js +1 -1
- package/dist/transforms/arguments-to-parameters.js.map +1 -1
- package/dist/transforms/arrow-to-function.js.map +1 -1
- package/dist/transforms/collapse-single-use-temps.js +7 -7
- package/dist/transforms/collapse-single-use-temps.js.map +1 -1
- package/dist/transforms/const-let-to-var.js +3 -3
- package/dist/transforms/const-let-to-var.js.map +1 -1
- package/dist/transforms/const-to-let.js.map +1 -1
- package/dist/transforms/do-while-to-while.js +3 -3
- package/dist/transforms/do-while-to-while.js.map +1 -1
- package/dist/transforms/drop-console.js +4 -3
- package/dist/transforms/drop-console.js.map +1 -1
- package/dist/transforms/drop-debugger.js.map +1 -1
- package/dist/transforms/expand-binary-assignment.js +5 -3
- package/dist/transforms/expand-binary-assignment.js.map +1 -1
- package/dist/transforms/expand-logical-assignment.js +5 -3
- package/dist/transforms/expand-logical-assignment.js.map +1 -1
- package/dist/transforms/extract-object-properties.js +7 -7
- package/dist/transforms/extract-object-properties.js.map +1 -1
- package/dist/transforms/fold-builtin-methods.js +16 -15
- 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/for-to-while.js +5 -5
- package/dist/transforms/for-to-while.js.map +1 -1
- package/dist/transforms/function-declaration-to-expression.js +3 -3
- package/dist/transforms/function-declaration-to-expression.js.map +1 -1
- package/dist/transforms/inline-functions.js +7 -7
- package/dist/transforms/inline-functions.js.map +1 -1
- package/dist/transforms/logical-to-ternary.js +2 -2
- package/dist/transforms/logical-to-ternary.js.map +1 -1
- package/dist/transforms/mangle-properties.d.ts.map +1 -1
- package/dist/transforms/mangle-properties.js +35 -86
- package/dist/transforms/mangle-properties.js.map +1 -1
- package/dist/transforms/nullish-coalescing-to-ternary.js +2 -2
- package/dist/transforms/nullish-coalescing-to-ternary.js.map +1 -1
- package/dist/transforms/numbers-to-strings.js +2 -2
- package/dist/transforms/numbers-to-strings.js.map +1 -1
- package/dist/transforms/object-method-to-property.js +1 -1
- package/dist/transforms/object-method-to-property.js.map +1 -1
- package/dist/transforms/optional-chaining-to-ternary.js +2 -2
- package/dist/transforms/optional-chaining-to-ternary.js.map +1 -1
- package/dist/transforms/pack-declarations-into-parameters.js +2 -2
- package/dist/transforms/pack-declarations-into-parameters.js.map +1 -1
- package/dist/transforms/pack.d.ts.map +1 -1
- package/dist/transforms/pack.js +194 -26
- package/dist/transforms/pack.js.map +1 -1
- package/dist/transforms/remove-anonymous-function-names.js +3 -3
- package/dist/transforms/remove-anonymous-function-names.js.map +1 -1
- package/dist/transforms/remove-unreachable-code.js +1 -1
- package/dist/transforms/remove-unreachable-code.js.map +1 -1
- package/dist/transforms/remove-unused-code.js +4 -4
- package/dist/transforms/remove-unused-code.js.map +1 -1
- package/dist/transforms/rename-identifiers.d.ts.map +1 -1
- package/dist/transforms/rename-identifiers.js +4 -3
- package/dist/transforms/rename-identifiers.js.map +1 -1
- package/dist/transforms/specials-to-strings.d.ts.map +1 -1
- package/dist/transforms/specials-to-strings.js +2 -2
- package/dist/transforms/specials-to-strings.js.map +1 -1
- package/dist/transforms/switch-to-if.js +7 -7
- package/dist/transforms/switch-to-if.js.map +1 -1
- package/dist/transforms/ternary-to-if.js +7 -4
- package/dist/transforms/ternary-to-if.js.map +1 -1
- package/dist/transforms/update-to-assignment.js +4 -2
- package/dist/transforms/update-to-assignment.js.map +1 -1
- package/dist/transforms/yodify-conditions.js +3 -3
- package/dist/transforms/yodify-conditions.js.map +1 -1
- package/dist/utils/ast.d.ts.map +1 -1
- package/dist/utils/ast.js +2 -1
- package/dist/utils/ast.js.map +1 -1
- package/dist/utils/evaluation-order.js.map +1 -1
- package/dist/utils/logical-lowering.js +2 -2
- package/dist/utils/logical-lowering.js.map +1 -1
- package/dist/utils/loop-lowering.js +1 -1
- package/dist/utils/loop-lowering.js.map +1 -1
- package/dist/utils/string-generator.js +1 -1
- package/dist/utils/string-generator.js.map +1 -1
- package/package.json +14 -12
- package/src/analysis/constant.ts +4 -4
- package/src/analysis/inlineability.ts +43 -35
- package/src/analysis/purity.ts +3 -3
- package/src/index.ts +4 -2
- package/src/obfuscator.ts +42 -42
- package/src/options.ts +8 -1
- package/src/preparation/expand-destructuring.ts +5 -4
- package/src/preparation/expand-template-literals.ts +4 -3
- package/src/preparation/split-sequence-expressions.ts +2 -2
- package/src/preparation/split-variable-declarations.ts +1 -1
- package/src/preparation/stringify-object-keys.ts +2 -2
- package/src/preparation/wrap-single-statements.ts +2 -2
- package/src/transforms/arguments-to-parameters.ts +5 -5
- package/src/transforms/arrow-to-function.ts +3 -3
- package/src/transforms/collapse-single-use-temps.ts +14 -14
- package/src/transforms/const-let-to-var.ts +6 -6
- package/src/transforms/const-to-let.ts +4 -4
- package/src/transforms/do-while-to-while.ts +6 -6
- package/src/transforms/drop-console.ts +8 -7
- package/src/transforms/drop-debugger.ts +2 -2
- package/src/transforms/expand-binary-assignment.ts +8 -6
- package/src/transforms/expand-logical-assignment.ts +8 -6
- package/src/transforms/extract-object-properties.ts +12 -12
- package/src/transforms/fold-builtin-methods.ts +20 -19
- package/src/transforms/fold-constants.ts +8 -8
- package/src/transforms/for-to-while.ts +8 -8
- package/src/transforms/function-declaration-to-expression.ts +5 -5
- package/src/transforms/inline-functions.ts +14 -14
- package/src/transforms/logical-to-ternary.ts +7 -7
- package/src/transforms/mangle-properties.ts +43 -97
- package/src/transforms/nullish-coalescing-to-ternary.ts +6 -6
- package/src/transforms/numbers-to-strings.ts +4 -4
- package/src/transforms/object-method-to-property.ts +4 -4
- package/src/transforms/optional-chaining-to-ternary.ts +5 -5
- package/src/transforms/pack-declarations-into-parameters.ts +7 -7
- package/src/transforms/pack.ts +212 -33
- package/src/transforms/remove-anonymous-function-names.ts +5 -5
- package/src/transforms/remove-unreachable-code.ts +3 -3
- package/src/transforms/remove-unused-code.ts +8 -8
- package/src/transforms/rename-identifiers.ts +9 -8
- package/src/transforms/specials-to-strings.ts +7 -7
- package/src/transforms/switch-to-if.ts +12 -12
- package/src/transforms/ternary-to-if.ts +12 -9
- package/src/transforms/update-to-assignment.ts +7 -5
- package/src/transforms/yodify-conditions.ts +6 -6
- package/src/utils/ast.ts +2 -1
- package/src/utils/evaluation-order.ts +1 -1
- package/src/utils/logical-lowering.ts +3 -3
- package/src/utils/loop-lowering.ts +1 -1
- package/src/utils/string-generator.ts +1 -1
package/src/analysis/constant.ts
CHANGED
|
@@ -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 '
|
|
9
|
-
import { isSideEffectFree } from '
|
|
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
|
|
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 {
|
|
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
|
-
//
|
|
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
|
|
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
|
-
|
|
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
|
|
81
|
-
|
|
82
|
-
|
|
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
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
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(
|
|
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(
|
|
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
|
|
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
|
package/src/analysis/purity.ts
CHANGED
|
@@ -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 '
|
|
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 '
|
|
1
|
+
export { obfuscate } from './obfuscator'
|
|
2
2
|
export type {
|
|
3
3
|
ManglePropertiesOptions,
|
|
4
4
|
ObfuscatorOptions,
|
|
5
5
|
PackOptions,
|
|
6
6
|
PropertyNameGenerator,
|
|
7
|
-
|
|
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 '
|
|
7
|
-
import { dotToBracket } from '
|
|
8
|
-
import { expandDestructuring } from '
|
|
9
|
-
import { expandTemplateLiterals } from '
|
|
10
|
-
import { splitSequenceExpressions } from '
|
|
11
|
-
import { splitVariableDeclarations } from '
|
|
12
|
-
import { stringifyObjectKeys } from '
|
|
13
|
-
import { wrapSingleStatements } from '
|
|
14
|
-
import { argumentsToParameters } from '
|
|
15
|
-
import { arrowToFunction } from '
|
|
16
|
-
import { collapseSingleUseTemps } from '
|
|
17
|
-
import { constLetToVar } from '
|
|
18
|
-
import { constToLet } from '
|
|
19
|
-
import { doWhileToWhile } from '
|
|
20
|
-
import { dropConsole } from '
|
|
21
|
-
import { dropDebugger } from '
|
|
22
|
-
import { expandBinaryAssignment } from '
|
|
23
|
-
import { expandLogicalAssignment } from '
|
|
24
|
-
import { extractObjectProperties } from '
|
|
25
|
-
import { foldBuiltinMethods } from '
|
|
26
|
-
import { foldConstants } from '
|
|
27
|
-
import { forToWhile } from '
|
|
28
|
-
import { functionDeclarationToExpression } from '
|
|
29
|
-
import { inlineFunctions } from '
|
|
30
|
-
import { logicalToTernary } from '
|
|
31
|
-
import { mangleProperties } from '
|
|
32
|
-
import { nullishCoalescingToTernary } from '
|
|
33
|
-
import { numbersToStrings } from '
|
|
34
|
-
import { objectMethodToProperty } from '
|
|
35
|
-
import { optionalChainingToTernary } from '
|
|
36
|
-
import { pack } from '
|
|
37
|
-
import { packDeclarationsIntoParameters } from '
|
|
38
|
-
import { removeAnonymousFunctionNames } from '
|
|
39
|
-
import { removeUnreachableCode } from '
|
|
40
|
-
import { removeUnusedCode } from '
|
|
41
|
-
import { renameIdentifiers } from '
|
|
42
|
-
import { specialsToStrings } from '
|
|
43
|
-
import { switchToIf } from '
|
|
44
|
-
import { ternaryToIf } from '
|
|
45
|
-
import { updateToAssignment } from '
|
|
46
|
-
import { yodifyConditions } from '
|
|
47
|
-
import type { StringGeneratorModeOption } from '
|
|
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 '
|
|
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 '
|
|
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
|
|
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
|
|
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
|
|
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 '
|
|
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
|
|
34
|
+
const parent = path.parent
|
|
35
35
|
if (t.isTaggedTemplateExpression(parent) && parent.quasi === path.node) {
|
|
36
36
|
return
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
const
|
|
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 '
|
|
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
|
|
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
|
|
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 '
|
|
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
|
|
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
|
|
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
|
|
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 '
|
|
6
|
-
import { traverseForChanges } from '
|
|
7
|
-
import type { ChangeState } from '
|
|
8
|
-
import { isInStrictContext } from '
|
|
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
|
|
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 '
|
|
6
|
-
import { traverseForChanges } from '
|
|
7
|
-
import type { ChangeState } from '
|
|
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 '
|
|
6
|
+
import { evaluateConstant } from '../analysis/constant'
|
|
7
7
|
import {
|
|
8
8
|
declarationReaches,
|
|
9
9
|
initializerReaches,
|
|
10
10
|
readCrossesFunctionBoundary,
|
|
11
|
-
} from '
|
|
12
|
-
import { isPure } from '
|
|
13
|
-
import { isInsideWith } from '
|
|
14
|
-
import { reorderableToStatement } from '
|
|
15
|
-
import { valueToLiteral } from '
|
|
16
|
-
import { isCalleeOrTagOf } from '
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 '
|
|
7
|
-
import { enclosingScopeHasDirectEval, isInsideWith } from '
|
|
8
|
-
import { hasConstantViolation } from '
|
|
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
|
|
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
|
|
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
|
|
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 '
|
|
5
|
-
import { traverseForChanges } from '
|
|
6
|
-
import type { ChangeState } from '
|
|
7
|
-
import { hasConstantViolation } from '
|
|
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 '
|
|
6
|
-
import type { ChangeState } from '
|
|
7
|
-
import { collectLoopLabels, generateLoopLabel, redirectContinues } from '
|
|
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
|
|
45
|
-
const
|
|
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
|
|
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.
|