webfuscator 0.9.4
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/LICENSE +21 -0
- package/README.md +83 -0
- package/dist/analysis/constant.d.ts +11 -0
- package/dist/analysis/constant.d.ts.map +1 -0
- package/dist/analysis/constant.js +261 -0
- package/dist/analysis/constant.js.map +1 -0
- package/dist/analysis/document-order.d.ts +5 -0
- package/dist/analysis/document-order.d.ts.map +1 -0
- package/dist/analysis/document-order.js +124 -0
- package/dist/analysis/document-order.js.map +1 -0
- package/dist/analysis/inlineability.d.ts +13 -0
- package/dist/analysis/inlineability.d.ts.map +1 -0
- package/dist/analysis/inlineability.js +270 -0
- package/dist/analysis/inlineability.js.map +1 -0
- package/dist/analysis/purity.d.ts +5 -0
- package/dist/analysis/purity.d.ts.map +1 -0
- package/dist/analysis/purity.js +281 -0
- package/dist/analysis/purity.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/obfuscator.d.ts +4 -0
- package/dist/obfuscator.d.ts.map +1 -0
- package/dist/obfuscator.js +222 -0
- package/dist/obfuscator.js.map +1 -0
- package/dist/options.d.ts +73 -0
- package/dist/options.d.ts.map +1 -0
- package/dist/options.js +2 -0
- package/dist/options.js.map +1 -0
- package/dist/preparation/dot-to-bracket.d.ts +16 -0
- package/dist/preparation/dot-to-bracket.d.ts.map +1 -0
- package/dist/preparation/dot-to-bracket.js +36 -0
- package/dist/preparation/dot-to-bracket.js.map +1 -0
- package/dist/preparation/expand-destructuring.d.ts +26 -0
- package/dist/preparation/expand-destructuring.d.ts.map +1 -0
- package/dist/preparation/expand-destructuring.js +154 -0
- package/dist/preparation/expand-destructuring.js.map +1 -0
- package/dist/preparation/expand-template-literals.d.ts +23 -0
- package/dist/preparation/expand-template-literals.d.ts.map +1 -0
- package/dist/preparation/expand-template-literals.js +78 -0
- package/dist/preparation/expand-template-literals.js.map +1 -0
- package/dist/preparation/rewrite-class-fields.d.ts +43 -0
- package/dist/preparation/rewrite-class-fields.d.ts.map +1 -0
- package/dist/preparation/rewrite-class-fields.js +270 -0
- package/dist/preparation/rewrite-class-fields.js.map +1 -0
- package/dist/preparation/split-sequence-expressions.d.ts +15 -0
- package/dist/preparation/split-sequence-expressions.d.ts.map +1 -0
- package/dist/preparation/split-sequence-expressions.js +36 -0
- package/dist/preparation/split-sequence-expressions.js.map +1 -0
- package/dist/preparation/split-variable-declarations.d.ts +20 -0
- package/dist/preparation/split-variable-declarations.d.ts.map +1 -0
- package/dist/preparation/split-variable-declarations.js +36 -0
- package/dist/preparation/split-variable-declarations.js.map +1 -0
- package/dist/preparation/stringify-object-keys.d.ts +21 -0
- package/dist/preparation/stringify-object-keys.d.ts.map +1 -0
- package/dist/preparation/stringify-object-keys.js +96 -0
- package/dist/preparation/stringify-object-keys.js.map +1 -0
- package/dist/preparation/wrap-single-statements.d.ts +24 -0
- package/dist/preparation/wrap-single-statements.d.ts.map +1 -0
- package/dist/preparation/wrap-single-statements.js +53 -0
- package/dist/preparation/wrap-single-statements.js.map +1 -0
- package/dist/transforms/arguments-to-parameters.d.ts +20 -0
- package/dist/transforms/arguments-to-parameters.d.ts.map +1 -0
- package/dist/transforms/arguments-to-parameters.js +162 -0
- package/dist/transforms/arguments-to-parameters.js.map +1 -0
- package/dist/transforms/arrow-to-function.d.ts +46 -0
- package/dist/transforms/arrow-to-function.d.ts.map +1 -0
- package/dist/transforms/arrow-to-function.js +203 -0
- package/dist/transforms/arrow-to-function.js.map +1 -0
- package/dist/transforms/collapse-single-use-temps.d.ts +16 -0
- package/dist/transforms/collapse-single-use-temps.d.ts.map +1 -0
- package/dist/transforms/collapse-single-use-temps.js +805 -0
- package/dist/transforms/collapse-single-use-temps.js.map +1 -0
- package/dist/transforms/const-let-to-var.d.ts +21 -0
- package/dist/transforms/const-let-to-var.d.ts.map +1 -0
- package/dist/transforms/const-let-to-var.js +217 -0
- package/dist/transforms/const-let-to-var.js.map +1 -0
- package/dist/transforms/const-to-let.d.ts +17 -0
- package/dist/transforms/const-to-let.d.ts.map +1 -0
- package/dist/transforms/const-to-let.js +33 -0
- package/dist/transforms/const-to-let.js.map +1 -0
- package/dist/transforms/do-while-to-while.d.ts +26 -0
- package/dist/transforms/do-while-to-while.d.ts.map +1 -0
- package/dist/transforms/do-while-to-while.js +54 -0
- package/dist/transforms/do-while-to-while.js.map +1 -0
- package/dist/transforms/drop-console.d.ts +27 -0
- package/dist/transforms/drop-console.d.ts.map +1 -0
- package/dist/transforms/drop-console.js +107 -0
- package/dist/transforms/drop-console.js.map +1 -0
- package/dist/transforms/drop-debugger.d.ts +18 -0
- package/dist/transforms/drop-debugger.d.ts.map +1 -0
- package/dist/transforms/drop-debugger.js +34 -0
- package/dist/transforms/drop-debugger.js.map +1 -0
- package/dist/transforms/expand-binary-assignment.d.ts +26 -0
- package/dist/transforms/expand-binary-assignment.d.ts.map +1 -0
- package/dist/transforms/expand-binary-assignment.js +78 -0
- package/dist/transforms/expand-binary-assignment.js.map +1 -0
- package/dist/transforms/expand-logical-assignment.d.ts +22 -0
- package/dist/transforms/expand-logical-assignment.d.ts.map +1 -0
- package/dist/transforms/expand-logical-assignment.js +73 -0
- package/dist/transforms/expand-logical-assignment.js.map +1 -0
- package/dist/transforms/extract-object-properties.d.ts +21 -0
- package/dist/transforms/extract-object-properties.d.ts.map +1 -0
- package/dist/transforms/extract-object-properties.js +340 -0
- package/dist/transforms/extract-object-properties.js.map +1 -0
- package/dist/transforms/fold-builtin-methods.d.ts +23 -0
- package/dist/transforms/fold-builtin-methods.d.ts.map +1 -0
- package/dist/transforms/fold-builtin-methods.js +839 -0
- package/dist/transforms/fold-builtin-methods.js.map +1 -0
- package/dist/transforms/fold-constants.d.ts +16 -0
- package/dist/transforms/fold-constants.d.ts.map +1 -0
- package/dist/transforms/fold-constants.js +421 -0
- package/dist/transforms/fold-constants.js.map +1 -0
- package/dist/transforms/for-to-while.d.ts +29 -0
- package/dist/transforms/for-to-while.d.ts.map +1 -0
- package/dist/transforms/for-to-while.js +91 -0
- package/dist/transforms/for-to-while.js.map +1 -0
- package/dist/transforms/function-declaration-to-expression.d.ts +28 -0
- package/dist/transforms/function-declaration-to-expression.d.ts.map +1 -0
- package/dist/transforms/function-declaration-to-expression.js +95 -0
- package/dist/transforms/function-declaration-to-expression.js.map +1 -0
- package/dist/transforms/inline-functions.d.ts +16 -0
- package/dist/transforms/inline-functions.d.ts.map +1 -0
- package/dist/transforms/inline-functions.js +896 -0
- package/dist/transforms/inline-functions.js.map +1 -0
- package/dist/transforms/logical-to-ternary.d.ts +20 -0
- package/dist/transforms/logical-to-ternary.d.ts.map +1 -0
- package/dist/transforms/logical-to-ternary.js +78 -0
- package/dist/transforms/logical-to-ternary.js.map +1 -0
- package/dist/transforms/mangle-properties.d.ts +27 -0
- package/dist/transforms/mangle-properties.d.ts.map +1 -0
- package/dist/transforms/mangle-properties.js +703 -0
- package/dist/transforms/mangle-properties.js.map +1 -0
- package/dist/transforms/nullish-coalescing-to-ternary.d.ts +18 -0
- package/dist/transforms/nullish-coalescing-to-ternary.d.ts.map +1 -0
- package/dist/transforms/nullish-coalescing-to-ternary.js +61 -0
- package/dist/transforms/nullish-coalescing-to-ternary.js.map +1 -0
- package/dist/transforms/numbers-to-strings.d.ts +19 -0
- package/dist/transforms/numbers-to-strings.d.ts.map +1 -0
- package/dist/transforms/numbers-to-strings.js +51 -0
- package/dist/transforms/numbers-to-strings.js.map +1 -0
- package/dist/transforms/object-method-to-property.d.ts +34 -0
- package/dist/transforms/object-method-to-property.d.ts.map +1 -0
- package/dist/transforms/object-method-to-property.js +78 -0
- package/dist/transforms/object-method-to-property.js.map +1 -0
- package/dist/transforms/optional-chaining-to-ternary.d.ts +19 -0
- package/dist/transforms/optional-chaining-to-ternary.d.ts.map +1 -0
- package/dist/transforms/optional-chaining-to-ternary.js +206 -0
- package/dist/transforms/optional-chaining-to-ternary.js.map +1 -0
- package/dist/transforms/pack-declarations-into-parameters.d.ts +22 -0
- package/dist/transforms/pack-declarations-into-parameters.d.ts.map +1 -0
- package/dist/transforms/pack-declarations-into-parameters.js +208 -0
- package/dist/transforms/pack-declarations-into-parameters.js.map +1 -0
- package/dist/transforms/remove-anonymous-function-names.d.ts +17 -0
- package/dist/transforms/remove-anonymous-function-names.d.ts.map +1 -0
- package/dist/transforms/remove-anonymous-function-names.js +72 -0
- package/dist/transforms/remove-anonymous-function-names.js.map +1 -0
- package/dist/transforms/remove-unreachable-code.d.ts +21 -0
- package/dist/transforms/remove-unreachable-code.d.ts.map +1 -0
- package/dist/transforms/remove-unreachable-code.js +103 -0
- package/dist/transforms/remove-unreachable-code.js.map +1 -0
- package/dist/transforms/remove-unused-code.d.ts +18 -0
- package/dist/transforms/remove-unused-code.d.ts.map +1 -0
- package/dist/transforms/remove-unused-code.js +249 -0
- package/dist/transforms/remove-unused-code.js.map +1 -0
- package/dist/transforms/rename-identifiers.d.ts +24 -0
- package/dist/transforms/rename-identifiers.d.ts.map +1 -0
- package/dist/transforms/rename-identifiers.js +264 -0
- package/dist/transforms/rename-identifiers.js.map +1 -0
- package/dist/transforms/specials-to-strings.d.ts +30 -0
- package/dist/transforms/specials-to-strings.d.ts.map +1 -0
- package/dist/transforms/specials-to-strings.js +101 -0
- package/dist/transforms/specials-to-strings.js.map +1 -0
- package/dist/transforms/switch-to-if.d.ts +28 -0
- package/dist/transforms/switch-to-if.d.ts.map +1 -0
- package/dist/transforms/switch-to-if.js +279 -0
- package/dist/transforms/switch-to-if.js.map +1 -0
- package/dist/transforms/ternary-to-if.d.ts +20 -0
- package/dist/transforms/ternary-to-if.d.ts.map +1 -0
- package/dist/transforms/ternary-to-if.js +315 -0
- package/dist/transforms/ternary-to-if.js.map +1 -0
- package/dist/transforms/update-to-assignment.d.ts +26 -0
- package/dist/transforms/update-to-assignment.d.ts.map +1 -0
- package/dist/transforms/update-to-assignment.js +120 -0
- package/dist/transforms/update-to-assignment.js.map +1 -0
- package/dist/transforms/yodify-conditions.d.ts +22 -0
- package/dist/transforms/yodify-conditions.d.ts.map +1 -0
- package/dist/transforms/yodify-conditions.js +91 -0
- package/dist/transforms/yodify-conditions.js.map +1 -0
- package/dist/utils/ast.d.ts +10 -0
- package/dist/utils/ast.d.ts.map +1 -0
- package/dist/utils/ast.js +112 -0
- package/dist/utils/ast.js.map +1 -0
- package/dist/utils/change-tracking.d.ts +8 -0
- package/dist/utils/change-tracking.d.ts.map +1 -0
- package/dist/utils/change-tracking.js +8 -0
- package/dist/utils/change-tracking.js.map +1 -0
- package/dist/utils/dom-properties.d.ts +2 -0
- package/dist/utils/dom-properties.d.ts.map +1 -0
- package/dist/utils/dom-properties.js +9252 -0
- package/dist/utils/dom-properties.js.map +1 -0
- package/dist/utils/evaluation-order.d.ts +4 -0
- package/dist/utils/evaluation-order.d.ts.map +1 -0
- package/dist/utils/evaluation-order.js +152 -0
- package/dist/utils/evaluation-order.js.map +1 -0
- package/dist/utils/literal.d.ts +5 -0
- package/dist/utils/literal.d.ts.map +1 -0
- package/dist/utils/literal.js +90 -0
- package/dist/utils/literal.js.map +1 -0
- package/dist/utils/logical-lowering.d.ts +4 -0
- package/dist/utils/logical-lowering.d.ts.map +1 -0
- package/dist/utils/logical-lowering.js +104 -0
- package/dist/utils/logical-lowering.js.map +1 -0
- package/dist/utils/loop-lowering.d.ts +23 -0
- package/dist/utils/loop-lowering.d.ts.map +1 -0
- package/dist/utils/loop-lowering.js +88 -0
- package/dist/utils/loop-lowering.js.map +1 -0
- package/dist/utils/paths.d.ts +9 -0
- package/dist/utils/paths.d.ts.map +1 -0
- package/dist/utils/paths.js +90 -0
- package/dist/utils/paths.js.map +1 -0
- package/dist/utils/random.d.ts +3 -0
- package/dist/utils/random.d.ts.map +1 -0
- package/dist/utils/random.js +22 -0
- package/dist/utils/random.js.map +1 -0
- package/dist/utils/string-generator.d.ts +37 -0
- package/dist/utils/string-generator.d.ts.map +1 -0
- package/dist/utils/string-generator.js +175 -0
- package/dist/utils/string-generator.js.map +1 -0
- package/package.json +76 -0
- package/src/analysis/constant.ts +310 -0
- package/src/analysis/document-order.ts +141 -0
- package/src/analysis/inlineability.ts +336 -0
- package/src/analysis/purity.ts +315 -0
- package/src/index.ts +2 -0
- package/src/obfuscator.ts +271 -0
- package/src/options.ts +114 -0
- package/src/preparation/dot-to-bracket.ts +41 -0
- package/src/preparation/expand-destructuring.ts +211 -0
- package/src/preparation/expand-template-literals.ts +92 -0
- package/src/preparation/rewrite-class-fields.ts +320 -0
- package/src/preparation/split-sequence-expressions.ts +40 -0
- package/src/preparation/split-variable-declarations.ts +39 -0
- package/src/preparation/stringify-object-keys.ts +107 -0
- package/src/preparation/wrap-single-statements.ts +63 -0
- package/src/transforms/arguments-to-parameters.ts +187 -0
- package/src/transforms/arrow-to-function.ts +225 -0
- package/src/transforms/collapse-single-use-temps.ts +957 -0
- package/src/transforms/const-let-to-var.ts +246 -0
- package/src/transforms/const-to-let.ts +38 -0
- package/src/transforms/do-while-to-while.ts +69 -0
- package/src/transforms/drop-console.ts +119 -0
- package/src/transforms/drop-debugger.ts +38 -0
- package/src/transforms/expand-binary-assignment.ts +94 -0
- package/src/transforms/expand-logical-assignment.ts +84 -0
- package/src/transforms/extract-object-properties.ts +404 -0
- package/src/transforms/fold-builtin-methods.ts +949 -0
- package/src/transforms/fold-constants.ts +462 -0
- package/src/transforms/for-to-while.ts +109 -0
- package/src/transforms/function-declaration-to-expression.ts +108 -0
- package/src/transforms/inline-functions.ts +1074 -0
- package/src/transforms/logical-to-ternary.ts +92 -0
- package/src/transforms/mangle-properties.ts +819 -0
- package/src/transforms/nullish-coalescing-to-ternary.ts +77 -0
- package/src/transforms/numbers-to-strings.ts +61 -0
- package/src/transforms/object-method-to-property.ts +86 -0
- package/src/transforms/optional-chaining-to-ternary.ts +225 -0
- package/src/transforms/pack-declarations-into-parameters.ts +233 -0
- package/src/transforms/remove-anonymous-function-names.ts +84 -0
- package/src/transforms/remove-unreachable-code.ts +115 -0
- package/src/transforms/remove-unused-code.ts +296 -0
- package/src/transforms/rename-identifiers.ts +289 -0
- package/src/transforms/specials-to-strings.ts +116 -0
- package/src/transforms/switch-to-if.ts +339 -0
- package/src/transforms/ternary-to-if.ts +387 -0
- package/src/transforms/update-to-assignment.ts +132 -0
- package/src/transforms/yodify-conditions.ts +103 -0
- package/src/utils/ast.ts +129 -0
- package/src/utils/change-tracking.ts +15 -0
- package/src/utils/dom-properties.ts +9252 -0
- package/src/utils/evaluation-order.ts +178 -0
- package/src/utils/literal.ts +96 -0
- package/src/utils/logical-lowering.ts +132 -0
- package/src/utils/loop-lowering.ts +109 -0
- package/src/utils/paths.ts +103 -0
- package/src/utils/random.ts +22 -0
- package/src/utils/string-generator.ts +206 -0
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
import generate from '@babel/generator'
|
|
2
|
+
import { parse } from '@babel/parser'
|
|
3
|
+
import type { ParserOptions } from '@babel/parser'
|
|
4
|
+
import type { File } from '@babel/types'
|
|
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 { rewriteClassFields } from 'src/preparation/rewrite-class-fields'
|
|
11
|
+
import { splitSequenceExpressions } from 'src/preparation/split-sequence-expressions'
|
|
12
|
+
import { splitVariableDeclarations } from 'src/preparation/split-variable-declarations'
|
|
13
|
+
import { stringifyObjectKeys } from 'src/preparation/stringify-object-keys'
|
|
14
|
+
import { wrapSingleStatements } from 'src/preparation/wrap-single-statements'
|
|
15
|
+
import { argumentsToParameters } from 'src/transforms/arguments-to-parameters'
|
|
16
|
+
import { arrowToFunction } from 'src/transforms/arrow-to-function'
|
|
17
|
+
import { collapseSingleUseTemps } from 'src/transforms/collapse-single-use-temps'
|
|
18
|
+
import { constLetToVar } from 'src/transforms/const-let-to-var'
|
|
19
|
+
import { constToLet } from 'src/transforms/const-to-let'
|
|
20
|
+
import { doWhileToWhile } from 'src/transforms/do-while-to-while'
|
|
21
|
+
import { dropConsole } from 'src/transforms/drop-console'
|
|
22
|
+
import { dropDebugger } from 'src/transforms/drop-debugger'
|
|
23
|
+
import { expandBinaryAssignment } from 'src/transforms/expand-binary-assignment'
|
|
24
|
+
import { expandLogicalAssignment } from 'src/transforms/expand-logical-assignment'
|
|
25
|
+
import { extractObjectProperties } from 'src/transforms/extract-object-properties'
|
|
26
|
+
import { foldBuiltinMethods } from 'src/transforms/fold-builtin-methods'
|
|
27
|
+
import { foldConstants } from 'src/transforms/fold-constants'
|
|
28
|
+
import { forToWhile } from 'src/transforms/for-to-while'
|
|
29
|
+
import { functionDeclarationToExpression } from 'src/transforms/function-declaration-to-expression'
|
|
30
|
+
import { inlineFunctions } from 'src/transforms/inline-functions'
|
|
31
|
+
import { logicalToTernary } from 'src/transforms/logical-to-ternary'
|
|
32
|
+
import { mangleProperties } from 'src/transforms/mangle-properties'
|
|
33
|
+
import { nullishCoalescingToTernary } from 'src/transforms/nullish-coalescing-to-ternary'
|
|
34
|
+
import { numbersToStrings } from 'src/transforms/numbers-to-strings'
|
|
35
|
+
import { objectMethodToProperty } from 'src/transforms/object-method-to-property'
|
|
36
|
+
import { optionalChainingToTernary } from 'src/transforms/optional-chaining-to-ternary'
|
|
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'
|
|
48
|
+
|
|
49
|
+
const parserOptions: ParserOptions = { sourceType: 'unambiguous' }
|
|
50
|
+
|
|
51
|
+
const PRESERVED_COMMENT_RE = /^!|@(?:license|preserve)\b|^#\s*source(?:Mapping)?URL=/iu
|
|
52
|
+
|
|
53
|
+
const DEFAULT_STRING_GENERATOR_MODE: StringGeneratorModeOption = 'mangled'
|
|
54
|
+
const DEFAULT_SEED = 0
|
|
55
|
+
|
|
56
|
+
// Real inputs settle in a handful of iterations. Hitting this cap signals an
|
|
57
|
+
// algorithmic bug; never raise it as a workaround.
|
|
58
|
+
const FIXED_POINT_ITERATION_CAP = 10
|
|
59
|
+
|
|
60
|
+
type TransformFn = (ast: File, ctx: TransformContext) => boolean
|
|
61
|
+
type Phase = readonly [name: string, fn: TransformFn]
|
|
62
|
+
type ConfigurablePhase = readonly [name: TransformName, fn: TransformFn]
|
|
63
|
+
|
|
64
|
+
// Mangling must see quoted access and class fields before preparation erases
|
|
65
|
+
// those shapes. It is opt-in because outside code may use the same properties.
|
|
66
|
+
const BEFORE_PREPARATION: readonly ConfigurablePhase[] = [['mangleProperties', mangleProperties]]
|
|
67
|
+
|
|
68
|
+
// Shape-normalizing passes. Always run; never exposed in `options.transforms`.
|
|
69
|
+
const PREPARATION: readonly Phase[] = [
|
|
70
|
+
['rewriteClassFields', asBoolean(rewriteClassFields)],
|
|
71
|
+
['expandDestructuring', asBoolean(expandDestructuring)],
|
|
72
|
+
['wrapSingleStatements', asBoolean(wrapSingleStatements)],
|
|
73
|
+
['splitSequenceExpressions', asBoolean(splitSequenceExpressions)],
|
|
74
|
+
['splitVariableDeclarations', asBoolean(splitVariableDeclarations)],
|
|
75
|
+
['expandTemplateLiterals', asBoolean(expandTemplateLiterals)],
|
|
76
|
+
['stringifyObjectKeys', asBoolean(stringifyObjectKeys)],
|
|
77
|
+
['dotToBracket', asBoolean(dotToBracket)],
|
|
78
|
+
]
|
|
79
|
+
|
|
80
|
+
// These one-shot passes prepare the fixed point. Lexical declarations become
|
|
81
|
+
// foldable function-scoped bindings, except at script top level where `var`
|
|
82
|
+
// would create a global property. `argumentsToParameters` precedes parameter
|
|
83
|
+
// renaming. Method demotion precedes extraction because any surviving
|
|
84
|
+
// `ObjectMethod` makes extraction refuse the whole literal.
|
|
85
|
+
const PRE_LOOP: readonly ConfigurablePhase[] = [
|
|
86
|
+
['dropDebugger', dropDebugger],
|
|
87
|
+
['dropConsole', dropConsole],
|
|
88
|
+
['constToLet', constToLet],
|
|
89
|
+
['constLetToVar', constLetToVar],
|
|
90
|
+
['argumentsToParameters', argumentsToParameters],
|
|
91
|
+
['objectMethodToProperty', objectMethodToProperty],
|
|
92
|
+
]
|
|
93
|
+
|
|
94
|
+
// Folds, removals, extraction, and inlining expose work for one another. Stop
|
|
95
|
+
// when a full round makes no change.
|
|
96
|
+
const FIXED_POINT: readonly ConfigurablePhase[] = [
|
|
97
|
+
['foldConstants', foldConstants],
|
|
98
|
+
['foldBuiltinMethods', foldBuiltinMethods],
|
|
99
|
+
['extractObjectProperties', extractObjectProperties],
|
|
100
|
+
['inlineFunctions', inlineFunctions],
|
|
101
|
+
['removeUnreachableCode', removeUnreachableCode],
|
|
102
|
+
['removeUnusedCode', removeUnusedCode],
|
|
103
|
+
['removeAnonymousFunctionNames', removeAnonymousFunctionNames],
|
|
104
|
+
['collapseSingleUseTemps', collapseSingleUseTemps],
|
|
105
|
+
]
|
|
106
|
+
|
|
107
|
+
// Lowerings run from producer to consumer. Arrow capture lands before code can
|
|
108
|
+
// move out of an arrow. Function declarations remain available to the inliner
|
|
109
|
+
// until the fixed point ends. Loop passes converge on `while`. Updates become
|
|
110
|
+
// compound assignments before binary assignment expansion. Logical assignment,
|
|
111
|
+
// optional chaining, nullish coalescing, logical expressions, and ternaries then
|
|
112
|
+
// lower in that order.
|
|
113
|
+
const LOWERING: readonly ConfigurablePhase[] = [
|
|
114
|
+
['arrowToFunction', arrowToFunction],
|
|
115
|
+
['functionDeclarationToExpression', functionDeclarationToExpression],
|
|
116
|
+
['switchToIf', switchToIf],
|
|
117
|
+
['forToWhile', forToWhile],
|
|
118
|
+
['doWhileToWhile', doWhileToWhile],
|
|
119
|
+
['updateToAssignment', updateToAssignment],
|
|
120
|
+
['expandBinaryAssignment', expandBinaryAssignment],
|
|
121
|
+
['expandLogicalAssignment', expandLogicalAssignment],
|
|
122
|
+
['optionalChainingToTernary', optionalChainingToTernary],
|
|
123
|
+
['nullishCoalescingToTernary', nullishCoalescingToTernary],
|
|
124
|
+
['logicalToTernary', logicalToTernary],
|
|
125
|
+
['ternaryToIf', ternaryToIf],
|
|
126
|
+
]
|
|
127
|
+
|
|
128
|
+
// Yodification and folding need raw numeric literals. Special-value lowering
|
|
129
|
+
// comes next, then number lowering also handles the `1` created for `Infinity`.
|
|
130
|
+
const POST_PROCESS: readonly ConfigurablePhase[] = [
|
|
131
|
+
['packDeclarationsIntoParameters', packDeclarationsIntoParameters],
|
|
132
|
+
['yodifyConditions', yodifyConditions],
|
|
133
|
+
['specialsToStrings', specialsToStrings],
|
|
134
|
+
['numbersToStrings', numbersToStrings],
|
|
135
|
+
]
|
|
136
|
+
|
|
137
|
+
// Renaming runs last because later transforms depend on recognizable bindings.
|
|
138
|
+
const RENAME: readonly ConfigurablePhase[] = [['renameIdentifiers', renameIdentifiers]]
|
|
139
|
+
|
|
140
|
+
/** Parses, transforms, and prints JavaScript without mutating external state. */
|
|
141
|
+
export function obfuscate(code: string, options: ObfuscatorOptions = {}): string {
|
|
142
|
+
const log = options.verbose ? makeLogger() : null
|
|
143
|
+
const baseCtx: TransformContext = {
|
|
144
|
+
seed: options.seed ?? DEFAULT_SEED,
|
|
145
|
+
stringGeneratorMode: options.stringGeneratorMode ?? DEFAULT_STRING_GENERATOR_MODE,
|
|
146
|
+
}
|
|
147
|
+
const ast = run(log, 'parse', () => parse(code, parserOptions))
|
|
148
|
+
|
|
149
|
+
for (const [name, fn] of BEFORE_PREPARATION) {
|
|
150
|
+
if (!isEnabled(name, options)) {
|
|
151
|
+
continue
|
|
152
|
+
}
|
|
153
|
+
run(log, name, () => fn(ast, ctxFor(name, options, baseCtx)))
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
for (const [name, fn] of PREPARATION) {
|
|
157
|
+
run(log, name, () => fn(ast, baseCtx))
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
for (const [name, fn] of PRE_LOOP) {
|
|
161
|
+
if (!isEnabled(name, options)) {
|
|
162
|
+
continue
|
|
163
|
+
}
|
|
164
|
+
run(log, name, () => fn(ast, ctxFor(name, options, baseCtx)))
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
runFixedPoint(ast, options, baseCtx, log)
|
|
168
|
+
|
|
169
|
+
for (const [name, fn] of LOWERING) {
|
|
170
|
+
if (!isEnabled(name, options)) {
|
|
171
|
+
continue
|
|
172
|
+
}
|
|
173
|
+
run(log, name, () => fn(ast, ctxFor(name, options, baseCtx)))
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
for (const [name, fn] of POST_PROCESS) {
|
|
177
|
+
if (!isEnabled(name, options)) {
|
|
178
|
+
continue
|
|
179
|
+
}
|
|
180
|
+
run(log, name, () => fn(ast, ctxFor(name, options, baseCtx)))
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
for (const [name, fn] of RENAME) {
|
|
184
|
+
if (!isEnabled(name, options)) {
|
|
185
|
+
continue
|
|
186
|
+
}
|
|
187
|
+
run(log, name, () => fn(ast, ctxFor(name, options, baseCtx)))
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return run(log, 'generate', () => generate(ast, { shouldPrintComment }).code)
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function isEnabled(name: TransformName, options: ObfuscatorOptions): boolean {
|
|
194
|
+
if (name === 'mangleProperties') {
|
|
195
|
+
return (
|
|
196
|
+
options.transforms?.mangleProperties !== undefined &&
|
|
197
|
+
options.transforms.mangleProperties !== false
|
|
198
|
+
)
|
|
199
|
+
}
|
|
200
|
+
return options.transforms?.[name] !== false
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Boolean entries inherit the base. An override replaces its string mode.
|
|
204
|
+
function ctxFor(
|
|
205
|
+
name: TransformName,
|
|
206
|
+
options: ObfuscatorOptions,
|
|
207
|
+
base: TransformContext,
|
|
208
|
+
): TransformContext {
|
|
209
|
+
const entry = options.transforms?.[name]
|
|
210
|
+
let context = base
|
|
211
|
+
if (entry && typeof entry === 'object' && entry.stringGeneratorMode !== undefined) {
|
|
212
|
+
context = { ...base, stringGeneratorMode: entry.stringGeneratorMode }
|
|
213
|
+
}
|
|
214
|
+
if (name === 'mangleProperties' && entry && typeof entry === 'object') {
|
|
215
|
+
return { ...context, mangleProperties: entry }
|
|
216
|
+
}
|
|
217
|
+
return context
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function runFixedPoint(
|
|
221
|
+
ast: File,
|
|
222
|
+
options: ObfuscatorOptions,
|
|
223
|
+
ctx: TransformContext,
|
|
224
|
+
log: Logger | null,
|
|
225
|
+
): void {
|
|
226
|
+
const enabled = FIXED_POINT.filter(([name]) => isEnabled(name, options))
|
|
227
|
+
if (enabled.length === 0) {
|
|
228
|
+
return
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
for (let iter = 0; iter < FIXED_POINT_ITERATION_CAP; iter++) {
|
|
232
|
+
let any = false
|
|
233
|
+
for (const [name, fn] of enabled) {
|
|
234
|
+
if (run(log, name, () => fn(ast, ctx))) {
|
|
235
|
+
any = true
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
if (!any) {
|
|
239
|
+
return
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
function asBoolean(fn: (ast: File) => unknown): TransformFn {
|
|
245
|
+
return (ast) => {
|
|
246
|
+
fn(ast)
|
|
247
|
+
return false
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
function shouldPrintComment(value: string): boolean {
|
|
252
|
+
return PRESERVED_COMMENT_RE.test(value)
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
type Logger = <T>(name: string, fn: () => T) => T
|
|
256
|
+
|
|
257
|
+
function run<T>(log: Logger | null, name: string, fn: () => T): T {
|
|
258
|
+
return log ? log(name, fn) : fn()
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
function makeLogger(): Logger {
|
|
262
|
+
const t0 = performance.now()
|
|
263
|
+
const stamp = (): string => `[${(performance.now() - t0).toFixed(0).padStart(6)}ms]`
|
|
264
|
+
return (name, fn) => {
|
|
265
|
+
console.error(`${stamp()} > ${name}`)
|
|
266
|
+
const start = performance.now()
|
|
267
|
+
const result = fn()
|
|
268
|
+
console.error(`${stamp()} < ${name} (${(performance.now() - start).toFixed(1)}ms)`)
|
|
269
|
+
return result
|
|
270
|
+
}
|
|
271
|
+
}
|
package/src/options.ts
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import type { StringGeneratorModeOption } from 'src/utils/string-generator'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Exported transform entry points. Preparation passes are always enabled and
|
|
5
|
+
* do not appear here.
|
|
6
|
+
*/
|
|
7
|
+
export type TransformName =
|
|
8
|
+
| 'dropConsole'
|
|
9
|
+
| 'dropDebugger'
|
|
10
|
+
| 'constToLet'
|
|
11
|
+
| 'constLetToVar'
|
|
12
|
+
| 'argumentsToParameters'
|
|
13
|
+
| 'objectMethodToProperty'
|
|
14
|
+
| 'foldConstants'
|
|
15
|
+
| 'foldBuiltinMethods'
|
|
16
|
+
| 'extractObjectProperties'
|
|
17
|
+
| 'inlineFunctions'
|
|
18
|
+
| 'removeUnreachableCode'
|
|
19
|
+
| 'removeUnusedCode'
|
|
20
|
+
| 'removeAnonymousFunctionNames'
|
|
21
|
+
| 'collapseSingleUseTemps'
|
|
22
|
+
| 'arrowToFunction'
|
|
23
|
+
| 'functionDeclarationToExpression'
|
|
24
|
+
| 'switchToIf'
|
|
25
|
+
| 'forToWhile'
|
|
26
|
+
| 'doWhileToWhile'
|
|
27
|
+
| 'updateToAssignment'
|
|
28
|
+
| 'expandBinaryAssignment'
|
|
29
|
+
| 'expandLogicalAssignment'
|
|
30
|
+
| 'optionalChainingToTernary'
|
|
31
|
+
| 'nullishCoalescingToTernary'
|
|
32
|
+
| 'logicalToTernary'
|
|
33
|
+
| 'ternaryToIf'
|
|
34
|
+
| 'packDeclarationsIntoParameters'
|
|
35
|
+
| 'yodifyConditions'
|
|
36
|
+
| 'specialsToStrings'
|
|
37
|
+
| 'numbersToStrings'
|
|
38
|
+
| 'mangleProperties'
|
|
39
|
+
| 'renameIdentifiers'
|
|
40
|
+
|
|
41
|
+
// An override enables the transform and replaces its string-generator mode.
|
|
42
|
+
type TransformEntry = boolean | { stringGeneratorMode?: StringGeneratorModeOption }
|
|
43
|
+
|
|
44
|
+
/** Maps a zero-based ordinal to a stable property name. */
|
|
45
|
+
export type PropertyNameGenerator = (index: number) => string
|
|
46
|
+
|
|
47
|
+
/** Terser-compatible property-mangling options using camel-case names. */
|
|
48
|
+
export interface ManglePropertiesOptions {
|
|
49
|
+
/** Allow names of JavaScript and DOM builtins to be mangled. Default `false`. */
|
|
50
|
+
builtins?: boolean
|
|
51
|
+
/** Reuse and extend mappings across calls or separately transformed files. */
|
|
52
|
+
cache?: Map<string, string>
|
|
53
|
+
/** Keep the original name visible as `_$name$suffix_`. Default `false`. */
|
|
54
|
+
debug?: boolean | string
|
|
55
|
+
/**
|
|
56
|
+
* Preserve quoted occurrences. `true` also reserves their names globally;
|
|
57
|
+
* `'strict'` lets unquoted occurrences of the same name be mangled.
|
|
58
|
+
*/
|
|
59
|
+
keepQuoted?: boolean | 'strict'
|
|
60
|
+
/** Custom property-name generator. It takes precedence over `stringGeneratorMode`. */
|
|
61
|
+
nameGenerator?: PropertyNameGenerator
|
|
62
|
+
/**
|
|
63
|
+
* Only names carrying `/*@__MANGLE_PROP__*\/` or `/*#__MANGLE_PROP__*\/` may
|
|
64
|
+
* be mangled. Default `false`.
|
|
65
|
+
*/
|
|
66
|
+
onlyAnnotated?: boolean
|
|
67
|
+
/** Only names already present in `cache` may be mangled. Default `false`. */
|
|
68
|
+
onlyCache?: boolean
|
|
69
|
+
/** Only property names matching this expression may be mangled. */
|
|
70
|
+
regex?: RegExp | string
|
|
71
|
+
/** Property names that must not be mangled. */
|
|
72
|
+
reserved?: readonly string[]
|
|
73
|
+
/** Override the default string-generator mode for property names. */
|
|
74
|
+
stringGeneratorMode?: StringGeneratorModeOption
|
|
75
|
+
/** Mangle accesses rooted at undeclared identifiers. Default `false`. */
|
|
76
|
+
undeclared?: boolean
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
type SimpleTransform = Exclude<
|
|
80
|
+
TransformName,
|
|
81
|
+
'mangleProperties' | 'renameIdentifiers' | 'specialsToStrings'
|
|
82
|
+
>
|
|
83
|
+
|
|
84
|
+
type TransformsMap = Partial<Record<SimpleTransform, boolean>> & {
|
|
85
|
+
mangleProperties?: boolean | ManglePropertiesOptions
|
|
86
|
+
renameIdentifiers?: TransformEntry
|
|
87
|
+
specialsToStrings?: TransformEntry
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export interface ObfuscatorOptions {
|
|
91
|
+
/** Logs each pipeline step and its elapsed milliseconds to stderr. */
|
|
92
|
+
verbose?: boolean
|
|
93
|
+
/**
|
|
94
|
+
* Per-transform toggles and string-generator overrides. `mangleProperties` is
|
|
95
|
+
* opt-in because property names can cross the program boundary. Other omitted
|
|
96
|
+
* transforms default to enabled.
|
|
97
|
+
*/
|
|
98
|
+
transforms?: TransformsMap
|
|
99
|
+
/**
|
|
100
|
+
* Default mode for generated identifiers, labels, and string contents. An
|
|
101
|
+
* array mixes modes uniformly. Individual transforms may override it through
|
|
102
|
+
* `transforms.<name>.stringGeneratorMode`. Default `'mangled'`.
|
|
103
|
+
*/
|
|
104
|
+
stringGeneratorMode?: StringGeneratorModeOption
|
|
105
|
+
/** Seed for any nondeterministic transform. Same seed, same output. Default `0`. */
|
|
106
|
+
seed?: number
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Resolved per-call defaults handed to every transform.
|
|
110
|
+
export interface TransformContext {
|
|
111
|
+
mangleProperties?: ManglePropertiesOptions
|
|
112
|
+
seed: number
|
|
113
|
+
stringGeneratorMode: StringGeneratorModeOption
|
|
114
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import traverse from '@babel/traverse'
|
|
2
|
+
import * as t from '@babel/types'
|
|
3
|
+
import type { File } from '@babel/types'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Rewrites `obj.foo` as `obj["foo"]`. Skips already-computed access and
|
|
7
|
+
* private fields; private-name semantics aren't expressible as a string key.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* // ◀️ before
|
|
11
|
+
* user.name = obj.foo.bar;
|
|
12
|
+
* var v = maybe?.value;
|
|
13
|
+
*
|
|
14
|
+
* // ▶️ after
|
|
15
|
+
* user["name"] = obj["foo"]["bar"];
|
|
16
|
+
* var v = maybe?.["value"];
|
|
17
|
+
*/
|
|
18
|
+
export function dotToBracket(ast: File): void {
|
|
19
|
+
traverse(ast, visitor as Parameters<typeof traverse>[1])
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const visitor = {
|
|
23
|
+
'MemberExpression|OptionalMemberExpression'(path: {
|
|
24
|
+
node: t.MemberExpression | t.OptionalMemberExpression
|
|
25
|
+
}) {
|
|
26
|
+
convert(path.node)
|
|
27
|
+
},
|
|
28
|
+
} as const
|
|
29
|
+
|
|
30
|
+
function convert(node: t.MemberExpression | t.OptionalMemberExpression): void {
|
|
31
|
+
if (node.computed) {
|
|
32
|
+
return
|
|
33
|
+
}
|
|
34
|
+
if (!t.isIdentifier(node.property)) {
|
|
35
|
+
return
|
|
36
|
+
}
|
|
37
|
+
// Babel 8 narrows members by `computed`, so both fields need widening.
|
|
38
|
+
const bracketed = node as t.MemberExpression | t.OptionalMemberExpression
|
|
39
|
+
bracketed.property = t.stringLiteral(node.property.name)
|
|
40
|
+
bracketed.computed = true
|
|
41
|
+
}
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import traverse from '@babel/traverse'
|
|
2
|
+
import type { NodePath, Scope } from '@babel/traverse'
|
|
3
|
+
import * as t from '@babel/types'
|
|
4
|
+
import type { File } from '@babel/types'
|
|
5
|
+
|
|
6
|
+
import { enclosingScopeHasDirectEval, isInsideWith } from 'src/utils/ast'
|
|
7
|
+
|
|
8
|
+
type Kind = t.VariableDeclaration['kind']
|
|
9
|
+
|
|
10
|
+
// Babel 8 omits `AssignmentPattern` from `LVal`, so list these targets directly.
|
|
11
|
+
type BindingTarget = t.Identifier | t.ObjectPattern | t.ArrayPattern | t.AssignmentPattern
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Rewrites object destructuring declarations as individual property reads.
|
|
15
|
+
* Array patterns stay intact because indexed reads cannot reproduce the iterator
|
|
16
|
+
* protocol.
|
|
17
|
+
*
|
|
18
|
+
* The source, computed keys, and getters keep their original evaluation counts.
|
|
19
|
+
* Defaults compare against `void 0` because `undefined` can be shadowed.
|
|
20
|
+
*
|
|
21
|
+
* `with` and direct `eval` can expose synthesized names that Babel cannot see, so
|
|
22
|
+
* those scopes are skipped. A `RequireObjectCoercible` guard runs before any
|
|
23
|
+
* computed key, matching ObjectBindingPattern evaluation order.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* // ◀️ before
|
|
27
|
+
* const { a, b: renamed, c = 3 } = obj;
|
|
28
|
+
*
|
|
29
|
+
* // ▶️ after
|
|
30
|
+
* const _ref = obj;
|
|
31
|
+
* const a = _ref.a;
|
|
32
|
+
* const renamed = _ref.b;
|
|
33
|
+
* const _c = _ref.c;
|
|
34
|
+
* const c = _c === void 0 ? 3 : _c;
|
|
35
|
+
*/
|
|
36
|
+
export function expandDestructuring(ast: File): void {
|
|
37
|
+
traverse(ast, visitor)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const visitor = {
|
|
41
|
+
VariableDeclaration(path: NodePath<t.VariableDeclaration>) {
|
|
42
|
+
if (typeof path.listKey !== 'string') {
|
|
43
|
+
return
|
|
44
|
+
}
|
|
45
|
+
// Babel cannot prove a generated name hidden from `with` or direct `eval`.
|
|
46
|
+
if (isInsideWith(path) || enclosingScopeHasDirectEval(path)) {
|
|
47
|
+
return
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const { kind } = path.node
|
|
51
|
+
const replacement: t.Statement[] = []
|
|
52
|
+
let changed = false
|
|
53
|
+
|
|
54
|
+
for (const declarator of path.node.declarations) {
|
|
55
|
+
const { id, init } = declarator
|
|
56
|
+
|
|
57
|
+
if (
|
|
58
|
+
!init ||
|
|
59
|
+
!t.isObjectPattern(id) ||
|
|
60
|
+
containsRestElement(id) ||
|
|
61
|
+
containsArrayPattern(id) ||
|
|
62
|
+
containsEmptyObjectPattern(id)
|
|
63
|
+
) {
|
|
64
|
+
replacement.push(t.variableDeclaration(kind, [declarator]))
|
|
65
|
+
continue
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
let source: t.Expression = init
|
|
69
|
+
// Snapshot a mutable identifier before reading more than one property.
|
|
70
|
+
if (t.isIdentifier(source) && topLevelReadCount(id) > 1) {
|
|
71
|
+
const temp = path.scope.generateUidIdentifier('ref')
|
|
72
|
+
replacement.push(t.variableDeclaration(kind, [t.variableDeclarator(temp, source)]))
|
|
73
|
+
source = t.cloneNode(temp)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
replacement.push(...expandPattern(kind, id, source, path.scope))
|
|
77
|
+
changed = true
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (changed) {
|
|
81
|
+
path.replaceWithMultiple(replacement)
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function expandPattern(
|
|
87
|
+
kind: Kind,
|
|
88
|
+
target: BindingTarget,
|
|
89
|
+
source: t.Expression,
|
|
90
|
+
scope: Scope,
|
|
91
|
+
): t.Statement[] {
|
|
92
|
+
if (t.isIdentifier(target)) {
|
|
93
|
+
return [t.variableDeclaration(kind, [t.variableDeclarator(target, source)])]
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (t.isAssignmentPattern(target)) {
|
|
97
|
+
const innerName =
|
|
98
|
+
t.isIdentifier(target.left) && t.isValidIdentifier(target.left.name)
|
|
99
|
+
? scope.generateUid(target.left.name)
|
|
100
|
+
: scope.generateUid('default')
|
|
101
|
+
const innerId = t.identifier(innerName)
|
|
102
|
+
const decls: t.Statement[] = [
|
|
103
|
+
t.variableDeclaration(kind, [t.variableDeclarator(t.cloneNode(innerId), source)]),
|
|
104
|
+
]
|
|
105
|
+
const defaulted = t.conditionalExpression(
|
|
106
|
+
// `void 0`, not the shadowable identifier `undefined`.
|
|
107
|
+
t.binaryExpression(
|
|
108
|
+
'===',
|
|
109
|
+
t.cloneNode(innerId),
|
|
110
|
+
t.unaryExpression('void', t.numericLiteral(0)),
|
|
111
|
+
),
|
|
112
|
+
target.right,
|
|
113
|
+
t.cloneNode(innerId),
|
|
114
|
+
)
|
|
115
|
+
decls.push(...expandPattern(kind, target.left as BindingTarget, defaulted, scope))
|
|
116
|
+
return decls
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (t.isObjectPattern(target)) {
|
|
120
|
+
const decls: t.Statement[] = []
|
|
121
|
+
// Snapshot before repeated or nested property evaluation.
|
|
122
|
+
let src = source
|
|
123
|
+
if (!t.isIdentifier(src)) {
|
|
124
|
+
const temp = scope.generateUidIdentifier('ref')
|
|
125
|
+
decls.push(t.variableDeclaration(kind, [t.variableDeclarator(temp, src)]))
|
|
126
|
+
src = t.cloneNode(temp)
|
|
127
|
+
}
|
|
128
|
+
// `({} = src)` reproduces RequireObjectCoercible before computed keys run.
|
|
129
|
+
if (hasComputedKey(target)) {
|
|
130
|
+
decls.push(
|
|
131
|
+
t.expressionStatement(t.assignmentExpression('=', t.objectPattern([]), t.cloneNode(src))),
|
|
132
|
+
)
|
|
133
|
+
}
|
|
134
|
+
for (const prop of target.properties) {
|
|
135
|
+
if (t.isRestElement(prop)) {
|
|
136
|
+
continue
|
|
137
|
+
}
|
|
138
|
+
const { key } = prop
|
|
139
|
+
const useComputed = prop.computed || !t.isIdentifier(key)
|
|
140
|
+
const access = t.memberExpression(
|
|
141
|
+
t.cloneNode(src),
|
|
142
|
+
t.cloneNode(key) as t.Expression,
|
|
143
|
+
useComputed,
|
|
144
|
+
)
|
|
145
|
+
decls.push(...expandPattern(kind, prop.value as BindingTarget, access, scope))
|
|
146
|
+
}
|
|
147
|
+
return decls
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return [t.variableDeclaration(kind, [t.variableDeclarator(target, source)])]
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function topLevelReadCount(pattern: t.ObjectPattern): number {
|
|
154
|
+
return pattern.properties.filter((prop) => !t.isRestElement(prop)).length
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function hasComputedKey(pattern: t.ObjectPattern): boolean {
|
|
158
|
+
return pattern.properties.some((prop) => t.isObjectProperty(prop) && prop.computed)
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// An empty pattern still throws on nullish input, with no property read to carry
|
|
162
|
+
// that check after expansion.
|
|
163
|
+
function containsEmptyObjectPattern(pattern: BindingTarget): boolean {
|
|
164
|
+
if (t.isObjectPattern(pattern)) {
|
|
165
|
+
return (
|
|
166
|
+
pattern.properties.length === 0 ||
|
|
167
|
+
pattern.properties.some(
|
|
168
|
+
(prop) =>
|
|
169
|
+
t.isObjectProperty(prop) && containsEmptyObjectPattern(prop.value as BindingTarget),
|
|
170
|
+
)
|
|
171
|
+
)
|
|
172
|
+
}
|
|
173
|
+
if (t.isAssignmentPattern(pattern)) {
|
|
174
|
+
return containsEmptyObjectPattern(pattern.left as BindingTarget)
|
|
175
|
+
}
|
|
176
|
+
return false
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function containsArrayPattern(pattern: BindingTarget): boolean {
|
|
180
|
+
if (t.isArrayPattern(pattern)) {
|
|
181
|
+
return true
|
|
182
|
+
}
|
|
183
|
+
if (t.isObjectPattern(pattern)) {
|
|
184
|
+
return pattern.properties.some(
|
|
185
|
+
(prop) => t.isObjectProperty(prop) && containsArrayPattern(prop.value as BindingTarget),
|
|
186
|
+
)
|
|
187
|
+
}
|
|
188
|
+
if (t.isAssignmentPattern(pattern)) {
|
|
189
|
+
return containsArrayPattern(pattern.left as BindingTarget)
|
|
190
|
+
}
|
|
191
|
+
return false
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function containsRestElement(pattern: BindingTarget): boolean {
|
|
195
|
+
if (t.isObjectPattern(pattern)) {
|
|
196
|
+
return pattern.properties.some(
|
|
197
|
+
(prop) =>
|
|
198
|
+
t.isRestElement(prop) ||
|
|
199
|
+
(t.isObjectProperty(prop) && containsRestElement(prop.value as BindingTarget)),
|
|
200
|
+
)
|
|
201
|
+
}
|
|
202
|
+
if (t.isArrayPattern(pattern)) {
|
|
203
|
+
return pattern.elements.some(
|
|
204
|
+
(el) => el !== null && (t.isRestElement(el) || containsRestElement(el as BindingTarget)),
|
|
205
|
+
)
|
|
206
|
+
}
|
|
207
|
+
if (t.isAssignmentPattern(pattern)) {
|
|
208
|
+
return containsRestElement(pattern.left as BindingTarget)
|
|
209
|
+
}
|
|
210
|
+
return false
|
|
211
|
+
}
|