js-confuser 2.0.0 → 2.0.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/.github/ISSUE_TEMPLATE/bug_report.md +43 -43
- package/.github/ISSUE_TEMPLATE/feature_request.md +20 -20
- package/.github/workflows/node.js.yml +28 -28
- package/.prettierrc +4 -4
- package/CHANGELOG.md +1015 -987
- package/CODE_OF_CONDUCT.md +131 -131
- package/CONTRIBUTING.md +52 -52
- package/LICENSE +21 -21
- package/Migration.md +72 -72
- package/README.md +86 -86
- package/dist/constants.js +43 -43
- package/dist/index.js +14 -23
- package/dist/obfuscator.js +31 -25
- package/dist/order.js +4 -4
- package/dist/presets.js +31 -31
- package/dist/templates/integrityTemplate.js +4 -4
- package/dist/templates/template.js +1 -2
- package/dist/transforms/astScrambler.js +1 -2
- package/dist/transforms/calculator.js +1 -2
- package/dist/transforms/controlFlowFlattening.js +60 -41
- package/dist/transforms/deadCode.js +1 -2
- package/dist/transforms/dispatcher.js +4 -5
- package/dist/transforms/extraction/duplicateLiteralsRemoval.js +1 -2
- package/dist/transforms/extraction/objectExtraction.js +1 -2
- package/dist/transforms/finalizer.js +1 -2
- package/dist/transforms/flatten.js +1 -2
- package/dist/transforms/identifier/globalConcealing.js +15 -2
- package/dist/transforms/identifier/movedDeclarations.js +8 -7
- package/dist/transforms/identifier/renameVariables.js +7 -7
- package/dist/transforms/lock/integrity.js +8 -9
- package/dist/transforms/lock/lock.js +1 -2
- package/dist/transforms/minify.js +11 -29
- package/dist/transforms/opaquePredicates.js +1 -2
- package/dist/transforms/pack.js +1 -2
- package/dist/transforms/plugin.js +18 -19
- package/dist/transforms/preparation.js +16 -16
- package/dist/transforms/renameLabels.js +1 -2
- package/dist/transforms/rgf.js +8 -9
- package/dist/transforms/shuffle.js +1 -2
- package/dist/transforms/string/encoding.js +1 -2
- package/dist/transforms/string/stringCompression.js +3 -4
- package/dist/transforms/string/stringConcealing.js +1 -2
- package/dist/transforms/string/stringEncoding.js +1 -2
- package/dist/transforms/variableMasking.js +1 -2
- package/dist/utils/NameGen.js +2 -2
- package/dist/utils/PredicateGen.js +1 -2
- package/dist/utils/ast-utils.js +87 -88
- package/dist/utils/function-utils.js +8 -8
- package/dist/utils/node.js +5 -6
- package/dist/utils/object-utils.js +4 -4
- package/dist/utils/random-utils.js +20 -20
- package/dist/utils/static-utils.js +1 -2
- package/dist/validateOptions.js +4 -7
- package/index.d.ts +17 -17
- package/package.json +61 -59
- package/src/constants.ts +168 -168
- package/src/index.ts +118 -118
- package/src/obfuscationResult.ts +49 -49
- package/src/obfuscator.ts +501 -497
- package/src/options.ts +407 -407
- package/src/order.ts +54 -54
- package/src/presets.ts +125 -125
- package/src/templates/bufferToStringTemplate.ts +57 -57
- package/src/templates/deadCodeTemplates.ts +1185 -1185
- package/src/templates/getGlobalTemplate.ts +76 -76
- package/src/templates/integrityTemplate.ts +64 -64
- package/src/templates/setFunctionLengthTemplate.ts +11 -11
- package/src/templates/stringCompressionTemplate.ts +20 -20
- package/src/templates/tamperProtectionTemplates.ts +120 -120
- package/src/templates/template.ts +224 -224
- package/src/transforms/astScrambler.ts +99 -99
- package/src/transforms/calculator.ts +99 -99
- package/src/transforms/controlFlowFlattening.ts +1716 -1680
- package/src/transforms/deadCode.ts +82 -82
- package/src/transforms/dispatcher.ts +450 -450
- package/src/transforms/extraction/duplicateLiteralsRemoval.ts +156 -158
- package/src/transforms/extraction/objectExtraction.ts +186 -186
- package/src/transforms/finalizer.ts +74 -74
- package/src/transforms/flatten.ts +421 -418
- package/src/transforms/identifier/globalConcealing.ts +315 -295
- package/src/transforms/identifier/movedDeclarations.ts +252 -251
- package/src/transforms/identifier/renameVariables.ts +328 -321
- package/src/transforms/lock/integrity.ts +117 -117
- package/src/transforms/lock/lock.ts +418 -418
- package/src/transforms/minify.ts +615 -629
- package/src/transforms/opaquePredicates.ts +100 -100
- package/src/transforms/pack.ts +239 -239
- package/src/transforms/plugin.ts +173 -173
- package/src/transforms/preparation.ts +349 -347
- package/src/transforms/renameLabels.ts +175 -175
- package/src/transforms/rgf.ts +322 -322
- package/src/transforms/shuffle.ts +82 -82
- package/src/transforms/string/encoding.ts +144 -144
- package/src/transforms/string/stringCompression.ts +128 -128
- package/src/transforms/string/stringConcealing.ts +312 -312
- package/src/transforms/string/stringEncoding.ts +80 -80
- package/src/transforms/string/stringSplitting.ts +77 -77
- package/src/transforms/variableMasking.ts +257 -257
- package/src/utils/IntGen.ts +33 -33
- package/src/utils/NameGen.ts +116 -116
- package/src/utils/PredicateGen.ts +61 -61
- package/src/utils/ast-utils.ts +663 -663
- package/src/utils/function-utils.ts +50 -50
- package/src/utils/gen-utils.ts +48 -48
- package/src/utils/node.ts +78 -78
- package/src/utils/object-utils.ts +21 -21
- package/src/utils/random-utils.ts +93 -93
- package/src/utils/static-utils.ts +66 -66
- package/src/validateOptions.ts +256 -259
- package/tsconfig.json +13 -14
- package/dist/probability.js +0 -1
- package/dist/transforms/functionOutlining.js +0 -230
- package/dist/utils/ControlObject.js +0 -125
|
@@ -1,224 +1,224 @@
|
|
|
1
|
-
import * as babelTypes from "@babel/types";
|
|
2
|
-
import { parse } from "@babel/parser";
|
|
3
|
-
import traverse from "@babel/traverse";
|
|
4
|
-
import { NodePath } from "@babel/traverse";
|
|
5
|
-
import { ok } from "assert";
|
|
6
|
-
import { getRandomString } from "../utils/random-utils";
|
|
7
|
-
import { NodeSymbol } from "../constants";
|
|
8
|
-
|
|
9
|
-
// Create a union type of the symbol keys in NodeSymbol
|
|
10
|
-
type NodeSymbolKeys = keyof {
|
|
11
|
-
[K in keyof NodeSymbol as K extends symbol ? K : never]: NodeSymbol[K];
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
export interface TemplateVariables {
|
|
15
|
-
[varName: string]:
|
|
16
|
-
| string
|
|
17
|
-
| number
|
|
18
|
-
| (() => babelTypes.Node | babelTypes.Node[] | Template)
|
|
19
|
-
| babelTypes.Node
|
|
20
|
-
| babelTypes.Node[]
|
|
21
|
-
| Template;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export default class Template {
|
|
25
|
-
private templates: string[];
|
|
26
|
-
private defaultVariables: TemplateVariables;
|
|
27
|
-
private requiredVariables: Set<string>;
|
|
28
|
-
private astVariableMappings: Map<string, string>;
|
|
29
|
-
private astIdentifierPrefix = "__t_" + getRandomString(6);
|
|
30
|
-
private symbols = new Set<NodeSymbolKeys>();
|
|
31
|
-
|
|
32
|
-
constructor(...templates: string[]) {
|
|
33
|
-
this.templates = templates;
|
|
34
|
-
this.defaultVariables = Object.create(null);
|
|
35
|
-
this.requiredVariables = new Set<string>();
|
|
36
|
-
|
|
37
|
-
this.findRequiredVariables();
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
addSymbols(...symbols: NodeSymbolKeys[]): this {
|
|
41
|
-
symbols.forEach((symbol) => {
|
|
42
|
-
this.symbols.add(symbol);
|
|
43
|
-
});
|
|
44
|
-
return this;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
setDefaultVariables(defaultVariables: TemplateVariables): this {
|
|
48
|
-
this.defaultVariables = defaultVariables;
|
|
49
|
-
return this;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
private findRequiredVariables() {
|
|
53
|
-
const matches = this.templates[0].match(/{[$A-Za-z0-9_]+}/g);
|
|
54
|
-
if (matches !== null) {
|
|
55
|
-
matches.forEach((variable) => {
|
|
56
|
-
const name = variable.slice(1, -1);
|
|
57
|
-
|
|
58
|
-
this.requiredVariables.add(name);
|
|
59
|
-
});
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
private interpolateTemplate(variables: TemplateVariables) {
|
|
64
|
-
const allVariables = { ...this.defaultVariables, ...variables };
|
|
65
|
-
|
|
66
|
-
for (const requiredVariable of this.requiredVariables) {
|
|
67
|
-
if (typeof allVariables[requiredVariable] === "undefined") {
|
|
68
|
-
throw new Error(
|
|
69
|
-
`${
|
|
70
|
-
this.templates[0]
|
|
71
|
-
} missing variable: ${requiredVariable} from ${JSON.stringify(
|
|
72
|
-
allVariables
|
|
73
|
-
)}`
|
|
74
|
-
);
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
const template =
|
|
79
|
-
this.templates[Math.floor(Math.random() * this.templates.length)];
|
|
80
|
-
let output = template;
|
|
81
|
-
|
|
82
|
-
this.astVariableMappings = new Map();
|
|
83
|
-
|
|
84
|
-
Object.keys(allVariables).forEach((name) => {
|
|
85
|
-
const bracketName = `{${name.replace("$", "\\$")}}`;
|
|
86
|
-
let value = allVariables[name] as string;
|
|
87
|
-
|
|
88
|
-
if (this.isASTVariable(value)) {
|
|
89
|
-
let astIdentifierName = this.astIdentifierPrefix + name;
|
|
90
|
-
this.astVariableMappings.set(name, astIdentifierName);
|
|
91
|
-
|
|
92
|
-
value = astIdentifierName;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
const reg = new RegExp(bracketName, "g");
|
|
96
|
-
output = output.replace(reg, value);
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
return { output, template };
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
private isASTVariable(variable: any): boolean {
|
|
103
|
-
return typeof variable !== "string" && typeof variable !== "number";
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
private interpolateAST(ast: babelTypes.Node, variables: TemplateVariables) {
|
|
107
|
-
if (this.astVariableMappings.size === 0) return;
|
|
108
|
-
|
|
109
|
-
const allVariables = { ...this.defaultVariables, ...variables };
|
|
110
|
-
const template = this;
|
|
111
|
-
|
|
112
|
-
// Reverse the lookup map
|
|
113
|
-
// Before {name -> __t_m4H6nk_name}
|
|
114
|
-
// After {__t_m4H6nk_name -> name}
|
|
115
|
-
const reverseMappings = new Map<string, string>();
|
|
116
|
-
this.astVariableMappings.forEach((value, key) => {
|
|
117
|
-
reverseMappings.set(value, key);
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
const insertedVariables = new Set<string>();
|
|
121
|
-
|
|
122
|
-
traverse(ast, {
|
|
123
|
-
Identifier(path: NodePath<babelTypes.Identifier>) {
|
|
124
|
-
const idName = path.node.name;
|
|
125
|
-
if (!idName.startsWith(template.astIdentifierPrefix)) return;
|
|
126
|
-
|
|
127
|
-
const variableName = reverseMappings.get(idName);
|
|
128
|
-
ok(variableName, `Variable ${idName} not found in mappings`);
|
|
129
|
-
|
|
130
|
-
let value = allVariables[variableName];
|
|
131
|
-
let isSingleUse = true; // Hard-coded nodes are deemed 'single use'
|
|
132
|
-
if (typeof value === "function") {
|
|
133
|
-
value = value();
|
|
134
|
-
isSingleUse = false;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
if (value instanceof Template) {
|
|
138
|
-
value = value.compile(allVariables);
|
|
139
|
-
isSingleUse = false;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
// Duplicate node check
|
|
143
|
-
if (isSingleUse) {
|
|
144
|
-
if (insertedVariables.has(variableName)) {
|
|
145
|
-
ok(false, "Duplicate node inserted for variable: " + variableName);
|
|
146
|
-
}
|
|
147
|
-
insertedVariables.add(variableName);
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
// Insert new nodes
|
|
151
|
-
if (!Array.isArray(value)) {
|
|
152
|
-
path.replaceWith(value as babelTypes.Node);
|
|
153
|
-
} else {
|
|
154
|
-
path.replaceWithMultiple(value as babelTypes.Node[]);
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
path.skip();
|
|
158
|
-
},
|
|
159
|
-
});
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
file(variables: TemplateVariables = {}): babelTypes.File {
|
|
163
|
-
const { output } = this.interpolateTemplate(variables);
|
|
164
|
-
|
|
165
|
-
let file: babelTypes.File;
|
|
166
|
-
try {
|
|
167
|
-
file = parse(output, {
|
|
168
|
-
sourceType: "module",
|
|
169
|
-
allowReturnOutsideFunction: true,
|
|
170
|
-
});
|
|
171
|
-
} catch (e) {
|
|
172
|
-
throw new Error(
|
|
173
|
-
output + "\n" + "Template failed to parse: " + (e as Error).message
|
|
174
|
-
);
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
this.interpolateAST(file, variables);
|
|
178
|
-
|
|
179
|
-
if (this.symbols.size > 0) {
|
|
180
|
-
file.program.body.forEach((node) => {
|
|
181
|
-
for (const symbol of this.symbols) {
|
|
182
|
-
node[symbol] = true;
|
|
183
|
-
}
|
|
184
|
-
});
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
return file;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
compile(variables: TemplateVariables = {}): babelTypes.Statement[] {
|
|
191
|
-
const file = this.file(variables);
|
|
192
|
-
|
|
193
|
-
return file.program.body;
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
single<T extends babelTypes.Statement>(variables: TemplateVariables = {}): T {
|
|
197
|
-
const nodes = this.compile(variables);
|
|
198
|
-
|
|
199
|
-
if (nodes.length !== 1) {
|
|
200
|
-
const filteredNodes = nodes.filter(
|
|
201
|
-
(node) => node.type !== "EmptyStatement"
|
|
202
|
-
);
|
|
203
|
-
ok(
|
|
204
|
-
filteredNodes.length === 1,
|
|
205
|
-
`Expected single node, got ${filteredNodes
|
|
206
|
-
.map((node) => node.type)
|
|
207
|
-
.join(", ")}`
|
|
208
|
-
);
|
|
209
|
-
return filteredNodes[0] as T;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
return nodes[0] as T;
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
expression<T extends babelTypes.Expression>(
|
|
216
|
-
variables: TemplateVariables = {}
|
|
217
|
-
): T {
|
|
218
|
-
const statement = this.single(variables);
|
|
219
|
-
|
|
220
|
-
babelTypes.assertExpressionStatement(statement);
|
|
221
|
-
|
|
222
|
-
return statement.expression as T;
|
|
223
|
-
}
|
|
224
|
-
}
|
|
1
|
+
import * as babelTypes from "@babel/types";
|
|
2
|
+
import { parse } from "@babel/parser";
|
|
3
|
+
import traverse from "@babel/traverse";
|
|
4
|
+
import { NodePath } from "@babel/traverse";
|
|
5
|
+
import { ok } from "assert";
|
|
6
|
+
import { getRandomString } from "../utils/random-utils";
|
|
7
|
+
import { NodeSymbol } from "../constants";
|
|
8
|
+
|
|
9
|
+
// Create a union type of the symbol keys in NodeSymbol
|
|
10
|
+
type NodeSymbolKeys = keyof {
|
|
11
|
+
[K in keyof NodeSymbol as K extends symbol ? K : never]: NodeSymbol[K];
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export interface TemplateVariables {
|
|
15
|
+
[varName: string]:
|
|
16
|
+
| string
|
|
17
|
+
| number
|
|
18
|
+
| (() => babelTypes.Node | babelTypes.Node[] | Template)
|
|
19
|
+
| babelTypes.Node
|
|
20
|
+
| babelTypes.Node[]
|
|
21
|
+
| Template;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export default class Template {
|
|
25
|
+
private templates: string[];
|
|
26
|
+
private defaultVariables: TemplateVariables;
|
|
27
|
+
private requiredVariables: Set<string>;
|
|
28
|
+
private astVariableMappings: Map<string, string>;
|
|
29
|
+
private astIdentifierPrefix = "__t_" + getRandomString(6);
|
|
30
|
+
private symbols = new Set<NodeSymbolKeys>();
|
|
31
|
+
|
|
32
|
+
constructor(...templates: string[]) {
|
|
33
|
+
this.templates = templates;
|
|
34
|
+
this.defaultVariables = Object.create(null);
|
|
35
|
+
this.requiredVariables = new Set<string>();
|
|
36
|
+
|
|
37
|
+
this.findRequiredVariables();
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
addSymbols(...symbols: NodeSymbolKeys[]): this {
|
|
41
|
+
symbols.forEach((symbol) => {
|
|
42
|
+
this.symbols.add(symbol);
|
|
43
|
+
});
|
|
44
|
+
return this;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
setDefaultVariables(defaultVariables: TemplateVariables): this {
|
|
48
|
+
this.defaultVariables = defaultVariables;
|
|
49
|
+
return this;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
private findRequiredVariables() {
|
|
53
|
+
const matches = this.templates[0].match(/{[$A-Za-z0-9_]+}/g);
|
|
54
|
+
if (matches !== null) {
|
|
55
|
+
matches.forEach((variable) => {
|
|
56
|
+
const name = variable.slice(1, -1);
|
|
57
|
+
|
|
58
|
+
this.requiredVariables.add(name);
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
private interpolateTemplate(variables: TemplateVariables) {
|
|
64
|
+
const allVariables = { ...this.defaultVariables, ...variables };
|
|
65
|
+
|
|
66
|
+
for (const requiredVariable of this.requiredVariables) {
|
|
67
|
+
if (typeof allVariables[requiredVariable] === "undefined") {
|
|
68
|
+
throw new Error(
|
|
69
|
+
`${
|
|
70
|
+
this.templates[0]
|
|
71
|
+
} missing variable: ${requiredVariable} from ${JSON.stringify(
|
|
72
|
+
allVariables
|
|
73
|
+
)}`
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const template =
|
|
79
|
+
this.templates[Math.floor(Math.random() * this.templates.length)];
|
|
80
|
+
let output = template;
|
|
81
|
+
|
|
82
|
+
this.astVariableMappings = new Map();
|
|
83
|
+
|
|
84
|
+
Object.keys(allVariables).forEach((name) => {
|
|
85
|
+
const bracketName = `{${name.replace("$", "\\$")}}`;
|
|
86
|
+
let value = allVariables[name] as string;
|
|
87
|
+
|
|
88
|
+
if (this.isASTVariable(value)) {
|
|
89
|
+
let astIdentifierName = this.astIdentifierPrefix + name;
|
|
90
|
+
this.astVariableMappings.set(name, astIdentifierName);
|
|
91
|
+
|
|
92
|
+
value = astIdentifierName;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const reg = new RegExp(bracketName, "g");
|
|
96
|
+
output = output.replace(reg, value);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
return { output, template };
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
private isASTVariable(variable: any): boolean {
|
|
103
|
+
return typeof variable !== "string" && typeof variable !== "number";
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
private interpolateAST(ast: babelTypes.Node, variables: TemplateVariables) {
|
|
107
|
+
if (this.astVariableMappings.size === 0) return;
|
|
108
|
+
|
|
109
|
+
const allVariables = { ...this.defaultVariables, ...variables };
|
|
110
|
+
const template = this;
|
|
111
|
+
|
|
112
|
+
// Reverse the lookup map
|
|
113
|
+
// Before {name -> __t_m4H6nk_name}
|
|
114
|
+
// After {__t_m4H6nk_name -> name}
|
|
115
|
+
const reverseMappings = new Map<string, string>();
|
|
116
|
+
this.astVariableMappings.forEach((value, key) => {
|
|
117
|
+
reverseMappings.set(value, key);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
const insertedVariables = new Set<string>();
|
|
121
|
+
|
|
122
|
+
traverse(ast, {
|
|
123
|
+
Identifier(path: NodePath<babelTypes.Identifier>) {
|
|
124
|
+
const idName = path.node.name;
|
|
125
|
+
if (!idName.startsWith(template.astIdentifierPrefix)) return;
|
|
126
|
+
|
|
127
|
+
const variableName = reverseMappings.get(idName);
|
|
128
|
+
ok(variableName, `Variable ${idName} not found in mappings`);
|
|
129
|
+
|
|
130
|
+
let value = allVariables[variableName];
|
|
131
|
+
let isSingleUse = true; // Hard-coded nodes are deemed 'single use'
|
|
132
|
+
if (typeof value === "function") {
|
|
133
|
+
value = value();
|
|
134
|
+
isSingleUse = false;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (value instanceof Template) {
|
|
138
|
+
value = value.compile(allVariables);
|
|
139
|
+
isSingleUse = false;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Duplicate node check
|
|
143
|
+
if (isSingleUse) {
|
|
144
|
+
if (insertedVariables.has(variableName)) {
|
|
145
|
+
ok(false, "Duplicate node inserted for variable: " + variableName);
|
|
146
|
+
}
|
|
147
|
+
insertedVariables.add(variableName);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Insert new nodes
|
|
151
|
+
if (!Array.isArray(value)) {
|
|
152
|
+
path.replaceWith(value as babelTypes.Node);
|
|
153
|
+
} else {
|
|
154
|
+
path.replaceWithMultiple(value as babelTypes.Node[]);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
path.skip();
|
|
158
|
+
},
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
file(variables: TemplateVariables = {}): babelTypes.File {
|
|
163
|
+
const { output } = this.interpolateTemplate(variables);
|
|
164
|
+
|
|
165
|
+
let file: babelTypes.File;
|
|
166
|
+
try {
|
|
167
|
+
file = parse(output, {
|
|
168
|
+
sourceType: "module",
|
|
169
|
+
allowReturnOutsideFunction: true,
|
|
170
|
+
});
|
|
171
|
+
} catch (e) {
|
|
172
|
+
throw new Error(
|
|
173
|
+
output + "\n" + "Template failed to parse: " + (e as Error).message
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
this.interpolateAST(file, variables);
|
|
178
|
+
|
|
179
|
+
if (this.symbols.size > 0) {
|
|
180
|
+
file.program.body.forEach((node) => {
|
|
181
|
+
for (const symbol of this.symbols) {
|
|
182
|
+
node[symbol] = true;
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return file;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
compile(variables: TemplateVariables = {}): babelTypes.Statement[] {
|
|
191
|
+
const file = this.file(variables);
|
|
192
|
+
|
|
193
|
+
return file.program.body;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
single<T extends babelTypes.Statement>(variables: TemplateVariables = {}): T {
|
|
197
|
+
const nodes = this.compile(variables);
|
|
198
|
+
|
|
199
|
+
if (nodes.length !== 1) {
|
|
200
|
+
const filteredNodes = nodes.filter(
|
|
201
|
+
(node) => node.type !== "EmptyStatement"
|
|
202
|
+
);
|
|
203
|
+
ok(
|
|
204
|
+
filteredNodes.length === 1,
|
|
205
|
+
`Expected single node, got ${filteredNodes
|
|
206
|
+
.map((node) => node.type)
|
|
207
|
+
.join(", ")}`
|
|
208
|
+
);
|
|
209
|
+
return filteredNodes[0] as T;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
return nodes[0] as T;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
expression<T extends babelTypes.Expression>(
|
|
216
|
+
variables: TemplateVariables = {}
|
|
217
|
+
): T {
|
|
218
|
+
const statement = this.single(variables);
|
|
219
|
+
|
|
220
|
+
babelTypes.assertExpressionStatement(statement);
|
|
221
|
+
|
|
222
|
+
return statement.expression as T;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
@@ -1,99 +1,99 @@
|
|
|
1
|
-
import { NodePath } from "@babel/traverse";
|
|
2
|
-
import { PluginArg, PluginObject } from "./plugin";
|
|
3
|
-
import * as t from "@babel/types";
|
|
4
|
-
import { ok } from "assert";
|
|
5
|
-
import { Order } from "../order";
|
|
6
|
-
import { NodeSymbol, SKIP } from "../constants";
|
|
7
|
-
import Template from "../templates/template";
|
|
8
|
-
import { append } from "../utils/ast-utils";
|
|
9
|
-
|
|
10
|
-
export default ({ Plugin }: PluginArg): PluginObject => {
|
|
11
|
-
const me = Plugin(Order.AstScrambler, {
|
|
12
|
-
changeData: {
|
|
13
|
-
expressions: 0,
|
|
14
|
-
},
|
|
15
|
-
});
|
|
16
|
-
var callExprName: string;
|
|
17
|
-
|
|
18
|
-
return {
|
|
19
|
-
visitor: {
|
|
20
|
-
"Block|SwitchCase": {
|
|
21
|
-
exit(_path) {
|
|
22
|
-
const path = _path as NodePath<t.Block | t.SwitchCase>;
|
|
23
|
-
const isProgram = path.isProgram();
|
|
24
|
-
|
|
25
|
-
let containerKey: string;
|
|
26
|
-
if (path.isSwitchCase()) {
|
|
27
|
-
containerKey = "consequent";
|
|
28
|
-
} else if (path.isBlock()) {
|
|
29
|
-
containerKey = "body";
|
|
30
|
-
}
|
|
31
|
-
var container: t.Statement[] = path.node[containerKey];
|
|
32
|
-
var newContainer: t.Statement[] = [];
|
|
33
|
-
|
|
34
|
-
ok(Array.isArray(container));
|
|
35
|
-
|
|
36
|
-
var expressions: t.Expression[] = [];
|
|
37
|
-
|
|
38
|
-
const flushExpressions = () => {
|
|
39
|
-
if (!expressions.length) return;
|
|
40
|
-
|
|
41
|
-
// Not enough expressions to require a call expression
|
|
42
|
-
if (expressions.length === 1) {
|
|
43
|
-
newContainer.push(t.expressionStatement(expressions[0]));
|
|
44
|
-
expressions = [];
|
|
45
|
-
return;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
if (!callExprName) {
|
|
49
|
-
callExprName = me.getPlaceholder() + "_ast";
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
me.changeData.expressions += expressions.length;
|
|
53
|
-
|
|
54
|
-
newContainer.push(
|
|
55
|
-
t.expressionStatement(
|
|
56
|
-
t.callExpression(t.identifier(callExprName), expressions)
|
|
57
|
-
)
|
|
58
|
-
);
|
|
59
|
-
expressions = [];
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
for (var statement of container) {
|
|
63
|
-
if (
|
|
64
|
-
// Preserve last expression at the top level
|
|
65
|
-
(isProgram ? statement !== container.at(-1) : true) &&
|
|
66
|
-
t.isExpressionStatement(statement) &&
|
|
67
|
-
!(statement as NodeSymbol)[SKIP]
|
|
68
|
-
) {
|
|
69
|
-
if (t.isSequenceExpression(statement.expression)) {
|
|
70
|
-
expressions.push(...statement.expression.expressions);
|
|
71
|
-
} else {
|
|
72
|
-
expressions.push(statement.expression);
|
|
73
|
-
}
|
|
74
|
-
} else {
|
|
75
|
-
flushExpressions();
|
|
76
|
-
newContainer.push(statement);
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
flushExpressions();
|
|
81
|
-
|
|
82
|
-
path.node[containerKey] = newContainer;
|
|
83
|
-
|
|
84
|
-
if (path.isProgram()) {
|
|
85
|
-
if (callExprName) {
|
|
86
|
-
var functionDeclaration = new Template(`
|
|
87
|
-
function ${callExprName}(){
|
|
88
|
-
${callExprName} = function(){};
|
|
89
|
-
}
|
|
90
|
-
`).single();
|
|
91
|
-
|
|
92
|
-
append(path, functionDeclaration);
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
},
|
|
96
|
-
},
|
|
97
|
-
},
|
|
98
|
-
};
|
|
99
|
-
};
|
|
1
|
+
import { NodePath } from "@babel/traverse";
|
|
2
|
+
import { PluginArg, PluginObject } from "./plugin";
|
|
3
|
+
import * as t from "@babel/types";
|
|
4
|
+
import { ok } from "assert";
|
|
5
|
+
import { Order } from "../order";
|
|
6
|
+
import { NodeSymbol, SKIP } from "../constants";
|
|
7
|
+
import Template from "../templates/template";
|
|
8
|
+
import { append } from "../utils/ast-utils";
|
|
9
|
+
|
|
10
|
+
export default ({ Plugin }: PluginArg): PluginObject => {
|
|
11
|
+
const me = Plugin(Order.AstScrambler, {
|
|
12
|
+
changeData: {
|
|
13
|
+
expressions: 0,
|
|
14
|
+
},
|
|
15
|
+
});
|
|
16
|
+
var callExprName: string;
|
|
17
|
+
|
|
18
|
+
return {
|
|
19
|
+
visitor: {
|
|
20
|
+
"Block|SwitchCase": {
|
|
21
|
+
exit(_path) {
|
|
22
|
+
const path = _path as NodePath<t.Block | t.SwitchCase>;
|
|
23
|
+
const isProgram = path.isProgram();
|
|
24
|
+
|
|
25
|
+
let containerKey: string;
|
|
26
|
+
if (path.isSwitchCase()) {
|
|
27
|
+
containerKey = "consequent";
|
|
28
|
+
} else if (path.isBlock()) {
|
|
29
|
+
containerKey = "body";
|
|
30
|
+
}
|
|
31
|
+
var container: t.Statement[] = path.node[containerKey];
|
|
32
|
+
var newContainer: t.Statement[] = [];
|
|
33
|
+
|
|
34
|
+
ok(Array.isArray(container));
|
|
35
|
+
|
|
36
|
+
var expressions: t.Expression[] = [];
|
|
37
|
+
|
|
38
|
+
const flushExpressions = () => {
|
|
39
|
+
if (!expressions.length) return;
|
|
40
|
+
|
|
41
|
+
// Not enough expressions to require a call expression
|
|
42
|
+
if (expressions.length === 1) {
|
|
43
|
+
newContainer.push(t.expressionStatement(expressions[0]));
|
|
44
|
+
expressions = [];
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (!callExprName) {
|
|
49
|
+
callExprName = me.getPlaceholder() + "_ast";
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
me.changeData.expressions += expressions.length;
|
|
53
|
+
|
|
54
|
+
newContainer.push(
|
|
55
|
+
t.expressionStatement(
|
|
56
|
+
t.callExpression(t.identifier(callExprName), expressions)
|
|
57
|
+
)
|
|
58
|
+
);
|
|
59
|
+
expressions = [];
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
for (var statement of container) {
|
|
63
|
+
if (
|
|
64
|
+
// Preserve last expression at the top level
|
|
65
|
+
(isProgram ? statement !== container.at(-1) : true) &&
|
|
66
|
+
t.isExpressionStatement(statement) &&
|
|
67
|
+
!(statement as NodeSymbol)[SKIP]
|
|
68
|
+
) {
|
|
69
|
+
if (t.isSequenceExpression(statement.expression)) {
|
|
70
|
+
expressions.push(...statement.expression.expressions);
|
|
71
|
+
} else {
|
|
72
|
+
expressions.push(statement.expression);
|
|
73
|
+
}
|
|
74
|
+
} else {
|
|
75
|
+
flushExpressions();
|
|
76
|
+
newContainer.push(statement);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
flushExpressions();
|
|
81
|
+
|
|
82
|
+
path.node[containerKey] = newContainer;
|
|
83
|
+
|
|
84
|
+
if (path.isProgram()) {
|
|
85
|
+
if (callExprName) {
|
|
86
|
+
var functionDeclaration = new Template(`
|
|
87
|
+
function ${callExprName}(){
|
|
88
|
+
${callExprName} = function(){};
|
|
89
|
+
}
|
|
90
|
+
`).single();
|
|
91
|
+
|
|
92
|
+
append(path, functionDeclaration);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
};
|
|
99
|
+
};
|