expose-kit 0.4.0 → 0.6.0
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 +80 -4
- package/dist/index.js +614 -33
- package/dist/package.json +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -105,7 +105,7 @@ With this alone:
|
|
|
105
105
|
### 3. Apply transforms step by step
|
|
106
106
|
|
|
107
107
|
After `safe-scope`, combine common techniques like:
|
|
108
|
-
- `expand-array` and more
|
|
108
|
+
- `expand-array`, `expand-object` and more
|
|
109
109
|
- legacy obfuscator-specific commands
|
|
110
110
|
|
|
111
111
|
After **each step**, run `parsable` again.
|
|
@@ -196,14 +196,90 @@ Args:
|
|
|
196
196
|
Target array variable name
|
|
197
197
|
- `--o, --output <file>`
|
|
198
198
|
Output file path
|
|
199
|
-
- No extension → `file.expand-array.js`
|
|
200
|
-
- With extension → `file.expand-array.<ext>`
|
|
201
199
|
|
|
202
200
|
Notes:
|
|
203
|
-
- Each replacement is validated by reparsing;
|
|
201
|
+
- Each replacement is validated by reparsing; unsafe replacements are skipped.
|
|
202
|
+
(This array is intended to be immutable, so caution is required)
|
|
204
203
|
|
|
205
204
|
---
|
|
206
205
|
|
|
206
|
+
### `expose expand-object`
|
|
207
|
+
|
|
208
|
+
Expand object property access for primitive values.
|
|
209
|
+
```js
|
|
210
|
+
const obj = { a: 1, b: 2 };
|
|
211
|
+
// before
|
|
212
|
+
console.log(obj.a, obj["b"]);
|
|
213
|
+
// after
|
|
214
|
+
console.log(1, 2);
|
|
215
|
+
```
|
|
216
|
+
Example is [here](https://github.com/evex-dev/expose-kit/tree/main/commands/expand-object/mocks).
|
|
217
|
+
|
|
218
|
+
```bash
|
|
219
|
+
expose expand-object path/to/file.js --target objectName --output path/to/file.expand-object.js
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
Args:
|
|
223
|
+
- `--target <name>`
|
|
224
|
+
Target object variable name
|
|
225
|
+
- `--o, --output <file>`
|
|
226
|
+
Output file path
|
|
227
|
+
|
|
228
|
+
Notes:
|
|
229
|
+
- Each replacement is validated by reparsing; unsafe replacements are skipped.
|
|
230
|
+
(This object is intended to be immutable, so caution is required)
|
|
231
|
+
|
|
232
|
+
---
|
|
233
|
+
|
|
234
|
+
### `expose object-packer`
|
|
235
|
+
|
|
236
|
+
Pack consecutive object property assignments into literals.
|
|
237
|
+
```js
|
|
238
|
+
const obj = {};
|
|
239
|
+
// before
|
|
240
|
+
obj.a = 0;
|
|
241
|
+
obj["b"] = 1;
|
|
242
|
+
// after
|
|
243
|
+
const obj = { a: 0, b: 1 };
|
|
244
|
+
```
|
|
245
|
+
Example is [here](https://github.com/evex-dev/expose-kit/tree/main/commands/object-packer/mocks).
|
|
246
|
+
|
|
247
|
+
```bash
|
|
248
|
+
expose object-packer path/to/file.js --output path/to/file.object-packer.js
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
Args:
|
|
252
|
+
- `--o, --output <file>`
|
|
253
|
+
Output file path
|
|
254
|
+
|
|
255
|
+
Notes:
|
|
256
|
+
- Packs only consecutive assignments following an empty object literal.
|
|
257
|
+
- Stops when a property value references the object itself.
|
|
258
|
+
|
|
259
|
+
---
|
|
260
|
+
|
|
261
|
+
### `expose remove-unused`
|
|
262
|
+
|
|
263
|
+
Remove unused variabless.
|
|
264
|
+
```js
|
|
265
|
+
// before
|
|
266
|
+
var a = 0, b = 1;
|
|
267
|
+
console.log(a);
|
|
268
|
+
// after
|
|
269
|
+
var a = 0;
|
|
270
|
+
console.log(a);
|
|
271
|
+
```
|
|
272
|
+
Example is [here](https://github.com/evex-dev/expose-kit/tree/main/commands/remove-unused/mocks).
|
|
273
|
+
|
|
274
|
+
```bash
|
|
275
|
+
expose remove-unused path/to/file.js --output path/to/file.remove-unused.js
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
Args:
|
|
279
|
+
- `--o, --output <file>`
|
|
280
|
+
Output file path
|
|
281
|
+
|
|
282
|
+
|
|
207
283
|
## Community & Support
|
|
208
284
|
|
|
209
285
|
- Missing a feature? → [Create an issue](https://github.com/EdamAme-x/expose-kit/issues)
|
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
// index.ts
|
|
4
4
|
import { Command } from "commander";
|
|
5
|
-
import { dirname as
|
|
5
|
+
import { dirname as dirname7, join as join7 } from "path";
|
|
6
6
|
import { fileURLToPath } from "url";
|
|
7
7
|
import chalk4 from "chalk";
|
|
8
8
|
|
|
@@ -364,15 +364,27 @@ var findTargetArray = (ast, targetName) => {
|
|
|
364
364
|
if (!found) return null;
|
|
365
365
|
let hasRiskOfSideEffects = false;
|
|
366
366
|
patchDefault(traverse2)(ast, {
|
|
367
|
-
|
|
367
|
+
MemberExpression(path) {
|
|
368
368
|
if (hasRiskOfSideEffects) return;
|
|
369
|
-
if (!t.isIdentifier(path.node, { name: targetName })) return;
|
|
369
|
+
if (!t.isIdentifier(path.node.object, { name: targetName })) return;
|
|
370
370
|
const parent = path.parentPath;
|
|
371
371
|
if (!parent) return;
|
|
372
|
-
if (
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
372
|
+
if (isAssignmentTarget(path)) {
|
|
373
|
+
hasRiskOfSideEffects = true;
|
|
374
|
+
}
|
|
375
|
+
},
|
|
376
|
+
AssignmentExpression(path) {
|
|
377
|
+
if (hasRiskOfSideEffects) return;
|
|
378
|
+
const left = path.get("left");
|
|
379
|
+
if (left.isIdentifier({ name: targetName })) {
|
|
380
|
+
hasRiskOfSideEffects = true;
|
|
381
|
+
}
|
|
382
|
+
},
|
|
383
|
+
UpdateExpression(path) {
|
|
384
|
+
if (hasRiskOfSideEffects) return;
|
|
385
|
+
if (t.isIdentifier(path.node.argument, { name: targetName })) {
|
|
386
|
+
hasRiskOfSideEffects = true;
|
|
387
|
+
}
|
|
376
388
|
}
|
|
377
389
|
});
|
|
378
390
|
const result = found;
|
|
@@ -386,7 +398,9 @@ var expandArrayAccess = async (code, filename, targetName) => {
|
|
|
386
398
|
throw new Error(`Target array '${targetName}' is not a primitive array`);
|
|
387
399
|
}
|
|
388
400
|
if (targetArray.hasRiskOfSideEffects) {
|
|
389
|
-
const continueAnswer = await createPrompt(
|
|
401
|
+
const continueAnswer = await createPrompt(
|
|
402
|
+
"The target array has risk of side effects, do you want to continue? (y/n)"
|
|
403
|
+
);
|
|
390
404
|
if (continueAnswer !== "y") {
|
|
391
405
|
throw new Error("User cancelled");
|
|
392
406
|
}
|
|
@@ -491,7 +505,7 @@ var expand_array_default = createCommand((program2) => {
|
|
|
491
505
|
);
|
|
492
506
|
});
|
|
493
507
|
|
|
494
|
-
// commands/
|
|
508
|
+
// commands/expand-object/index.ts
|
|
495
509
|
import { readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
|
|
496
510
|
import { basename as basename3, dirname as dirname3, extname as extname3, join as join3 } from "path";
|
|
497
511
|
import { parse as parse4 } from "@babel/parser";
|
|
@@ -502,10 +516,463 @@ import loading4 from "loading-cli";
|
|
|
502
516
|
var createDefaultOutputPath3 = (inputPath) => {
|
|
503
517
|
const ext = extname3(inputPath);
|
|
504
518
|
if (!ext) {
|
|
505
|
-
return `${inputPath}.
|
|
519
|
+
return `${inputPath}.expand-object.js`;
|
|
506
520
|
}
|
|
507
521
|
const base = basename3(inputPath, ext);
|
|
508
|
-
return join3(dirname3(inputPath), `${base}.
|
|
522
|
+
return join3(dirname3(inputPath), `${base}.expand-object${ext}`);
|
|
523
|
+
};
|
|
524
|
+
var getPropertyKeyFromObjectProperty = (property) => {
|
|
525
|
+
if (t2.isIdentifier(property.key)) {
|
|
526
|
+
return property.key.name;
|
|
527
|
+
}
|
|
528
|
+
if (t2.isStringLiteral(property.key)) {
|
|
529
|
+
return property.key.value;
|
|
530
|
+
}
|
|
531
|
+
if (t2.isNumericLiteral(property.key)) {
|
|
532
|
+
return String(property.key.value);
|
|
533
|
+
}
|
|
534
|
+
return null;
|
|
535
|
+
};
|
|
536
|
+
var getPropertyKeyFromMemberExpression = (node) => {
|
|
537
|
+
if (!node.computed && t2.isIdentifier(node.property)) {
|
|
538
|
+
return node.property.name;
|
|
539
|
+
}
|
|
540
|
+
if (!node.computed || !t2.isExpression(node.property)) {
|
|
541
|
+
return null;
|
|
542
|
+
}
|
|
543
|
+
if (t2.isStringLiteral(node.property)) {
|
|
544
|
+
return node.property.value;
|
|
545
|
+
}
|
|
546
|
+
if (t2.isNumericLiteral(node.property)) {
|
|
547
|
+
return String(node.property.value);
|
|
548
|
+
}
|
|
549
|
+
if (t2.isTemplateLiteral(node.property) && node.property.expressions.length === 0) {
|
|
550
|
+
return node.property.quasis[0]?.value.cooked ?? null;
|
|
551
|
+
}
|
|
552
|
+
return null;
|
|
553
|
+
};
|
|
554
|
+
var isAssignmentTarget2 = (path) => {
|
|
555
|
+
const parent = path.parentPath;
|
|
556
|
+
if (!parent) return false;
|
|
557
|
+
if (parent.isUpdateExpression()) return true;
|
|
558
|
+
if (parent.isAssignmentExpression() && parent.get("left") === path) {
|
|
559
|
+
return true;
|
|
560
|
+
}
|
|
561
|
+
if (parent.isForInStatement() && parent.get("left") === path) {
|
|
562
|
+
return true;
|
|
563
|
+
}
|
|
564
|
+
if (parent.isForOfStatement() && parent.get("left") === path) {
|
|
565
|
+
return true;
|
|
566
|
+
}
|
|
567
|
+
return false;
|
|
568
|
+
};
|
|
569
|
+
var collectMutatedProperties = (ast, targetName) => {
|
|
570
|
+
const mutatedProperties = /* @__PURE__ */ new Set();
|
|
571
|
+
let hasUnknownMutations = false;
|
|
572
|
+
patchDefault(traverse3)(ast, {
|
|
573
|
+
MemberExpression(path) {
|
|
574
|
+
if (!t2.isIdentifier(path.node.object, { name: targetName })) return;
|
|
575
|
+
const parent = path.parentPath;
|
|
576
|
+
if (!parent) return;
|
|
577
|
+
const isMutationTarget = parent.isUpdateExpression() || parent.isAssignmentExpression() && parent.get("left") === path || parent.isForInStatement() && parent.get("left") === path || parent.isForOfStatement() && parent.get("left") === path;
|
|
578
|
+
if (!isMutationTarget) return;
|
|
579
|
+
const propertyKey = getPropertyKeyFromMemberExpression(path.node);
|
|
580
|
+
if (!propertyKey) {
|
|
581
|
+
hasUnknownMutations = true;
|
|
582
|
+
return;
|
|
583
|
+
}
|
|
584
|
+
mutatedProperties.add(propertyKey);
|
|
585
|
+
}
|
|
586
|
+
});
|
|
587
|
+
return { mutatedProperties, hasUnknownMutations };
|
|
588
|
+
};
|
|
589
|
+
var getPropertyMap = (objectNode) => {
|
|
590
|
+
const map = /* @__PURE__ */ new Map();
|
|
591
|
+
for (const property of objectNode.properties) {
|
|
592
|
+
if (!t2.isObjectProperty(property)) {
|
|
593
|
+
return null;
|
|
594
|
+
}
|
|
595
|
+
if (property.computed) {
|
|
596
|
+
return null;
|
|
597
|
+
}
|
|
598
|
+
if (!t2.isExpression(property.value)) {
|
|
599
|
+
return null;
|
|
600
|
+
}
|
|
601
|
+
const key = getPropertyKeyFromObjectProperty(property);
|
|
602
|
+
if (!key) {
|
|
603
|
+
return null;
|
|
604
|
+
}
|
|
605
|
+
map.set(key, property.value);
|
|
606
|
+
}
|
|
607
|
+
return map;
|
|
608
|
+
};
|
|
609
|
+
var findTargetObject = (ast, targetName) => {
|
|
610
|
+
let found = null;
|
|
611
|
+
patchDefault(traverse3)(ast, {
|
|
612
|
+
VariableDeclarator(path) {
|
|
613
|
+
if (found) return;
|
|
614
|
+
if (!t2.isIdentifier(path.node.id, { name: targetName })) return;
|
|
615
|
+
if (!t2.isObjectExpression(path.node.init)) return;
|
|
616
|
+
const propertyMap = getPropertyMap(path.node.init);
|
|
617
|
+
if (!propertyMap) return;
|
|
618
|
+
found = {
|
|
619
|
+
binding: path.scope.getBinding(targetName) ?? null,
|
|
620
|
+
objectNode: path.node.init,
|
|
621
|
+
propertyMap
|
|
622
|
+
};
|
|
623
|
+
},
|
|
624
|
+
AssignmentExpression(path) {
|
|
625
|
+
if (found) return;
|
|
626
|
+
if (!t2.isIdentifier(path.node.left, { name: targetName })) return;
|
|
627
|
+
if (!t2.isObjectExpression(path.node.right)) return;
|
|
628
|
+
const propertyMap = getPropertyMap(path.node.right);
|
|
629
|
+
if (!propertyMap) return;
|
|
630
|
+
found = {
|
|
631
|
+
binding: path.scope.getBinding(targetName) ?? null,
|
|
632
|
+
objectNode: path.node.right,
|
|
633
|
+
propertyMap
|
|
634
|
+
};
|
|
635
|
+
}
|
|
636
|
+
});
|
|
637
|
+
if (!found) return null;
|
|
638
|
+
let hasRiskOfSideEffects = false;
|
|
639
|
+
patchDefault(traverse3)(ast, {
|
|
640
|
+
MemberExpression(path) {
|
|
641
|
+
if (hasRiskOfSideEffects) return;
|
|
642
|
+
if (!t2.isIdentifier(path.node.object, { name: targetName })) return;
|
|
643
|
+
const parent = path.parentPath;
|
|
644
|
+
if (!parent) return;
|
|
645
|
+
if (isAssignmentTarget2(path)) {
|
|
646
|
+
hasRiskOfSideEffects = true;
|
|
647
|
+
}
|
|
648
|
+
},
|
|
649
|
+
AssignmentExpression(path) {
|
|
650
|
+
if (hasRiskOfSideEffects) return;
|
|
651
|
+
const left = path.get("left");
|
|
652
|
+
if (left.isIdentifier({ name: targetName })) {
|
|
653
|
+
hasRiskOfSideEffects = true;
|
|
654
|
+
}
|
|
655
|
+
},
|
|
656
|
+
UpdateExpression(path) {
|
|
657
|
+
if (hasRiskOfSideEffects) return;
|
|
658
|
+
if (t2.isIdentifier(path.node.argument, { name: targetName })) {
|
|
659
|
+
hasRiskOfSideEffects = true;
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
});
|
|
663
|
+
const result = found;
|
|
664
|
+
result.hasRiskOfSideEffects = hasRiskOfSideEffects;
|
|
665
|
+
return result;
|
|
666
|
+
};
|
|
667
|
+
var expandObjectAccess = async (code, filename, targetName) => {
|
|
668
|
+
const ast = parse4(code, createParseOptions(filename));
|
|
669
|
+
const targetObject = findTargetObject(ast, targetName);
|
|
670
|
+
if (!targetObject) {
|
|
671
|
+
throw new Error(`Target object '${targetName}' is not a primitive object`);
|
|
672
|
+
}
|
|
673
|
+
if (targetObject.hasRiskOfSideEffects) {
|
|
674
|
+
const continueAnswer = await createPrompt(
|
|
675
|
+
"The target object has risk of side effects, do you want to continue? (y/n)"
|
|
676
|
+
);
|
|
677
|
+
if (continueAnswer !== "y") {
|
|
678
|
+
throw new Error("User cancelled");
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
const candidates = [];
|
|
682
|
+
const mutatedInfo = collectMutatedProperties(ast, targetName);
|
|
683
|
+
if (mutatedInfo.hasUnknownMutations) {
|
|
684
|
+
return {
|
|
685
|
+
code: patchDefault(generate3)(ast).code,
|
|
686
|
+
replacedCount: 0
|
|
687
|
+
};
|
|
688
|
+
}
|
|
689
|
+
patchDefault(traverse3)(ast, {
|
|
690
|
+
MemberExpression(path) {
|
|
691
|
+
if (isAssignmentTarget2(path)) return;
|
|
692
|
+
if (!t2.isIdentifier(path.node.object, { name: targetName })) return;
|
|
693
|
+
if (targetObject.binding && path.scope.getBinding(targetName) !== targetObject.binding) {
|
|
694
|
+
return;
|
|
695
|
+
}
|
|
696
|
+
const propertyKey = getPropertyKeyFromMemberExpression(path.node);
|
|
697
|
+
if (!propertyKey) return;
|
|
698
|
+
if (mutatedInfo.mutatedProperties.has(propertyKey)) return;
|
|
699
|
+
const replacement = targetObject.propertyMap.get(propertyKey);
|
|
700
|
+
if (!replacement) return;
|
|
701
|
+
candidates.push({
|
|
702
|
+
path,
|
|
703
|
+
replacement: t2.cloneNode(replacement, true)
|
|
704
|
+
});
|
|
705
|
+
}
|
|
706
|
+
});
|
|
707
|
+
let replacedCount = 0;
|
|
708
|
+
for (const candidate of candidates) {
|
|
709
|
+
const original = t2.cloneNode(candidate.path.node, true);
|
|
710
|
+
candidate.path.replaceWith(t2.cloneNode(candidate.replacement, true));
|
|
711
|
+
const nextCode = patchDefault(generate3)(ast).code;
|
|
712
|
+
try {
|
|
713
|
+
parse4(nextCode, createParseOptions(filename));
|
|
714
|
+
replacedCount += 1;
|
|
715
|
+
} catch {
|
|
716
|
+
candidate.path.replaceWith(original);
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
return {
|
|
720
|
+
code: patchDefault(generate3)(ast).code,
|
|
721
|
+
replacedCount
|
|
722
|
+
};
|
|
723
|
+
};
|
|
724
|
+
var expand_object_default = createCommand((program2) => {
|
|
725
|
+
program2.command("expand-object").description("Expand object property access for primitive values").argument("[file]", "The file to transform").option("--input, --file <file>", "The file to transform").option("--target <name>", "Target object variable name").option("--o, --output <file>", "Output file path").option("--unlimited", "Unlimited timeout").action(
|
|
726
|
+
async (fileArgument, options) => {
|
|
727
|
+
await timeout(
|
|
728
|
+
async ({ finish }) => {
|
|
729
|
+
const filename = fileArgument ?? options.file ?? await createPrompt("Enter the file path:");
|
|
730
|
+
if (!filename) {
|
|
731
|
+
showError("No file provided");
|
|
732
|
+
return finish();
|
|
733
|
+
}
|
|
734
|
+
const targetName = options.target ?? await createPrompt("Enter the target variable name:");
|
|
735
|
+
if (!targetName) {
|
|
736
|
+
showError("No target variable provided");
|
|
737
|
+
return finish();
|
|
738
|
+
}
|
|
739
|
+
try {
|
|
740
|
+
const fileContent = readFileSync4(filename, "utf8");
|
|
741
|
+
const defaultOutputPath = createDefaultOutputPath3(filename);
|
|
742
|
+
let outputPath = options.output;
|
|
743
|
+
if (!outputPath) {
|
|
744
|
+
const promptPath = (await createPrompt("Enter the output file path:"))?.trim();
|
|
745
|
+
outputPath = promptPath || defaultOutputPath;
|
|
746
|
+
}
|
|
747
|
+
const loader = loading4("Expanding object access...").start();
|
|
748
|
+
try {
|
|
749
|
+
const { code: output, replacedCount } = await expandObjectAccess(
|
|
750
|
+
fileContent,
|
|
751
|
+
filename,
|
|
752
|
+
targetName
|
|
753
|
+
);
|
|
754
|
+
writeFileSync3(outputPath, output, "utf8");
|
|
755
|
+
loader.succeed(
|
|
756
|
+
`Saved expand-object file to: ${outputPath} (${diff(fileContent, output).length} lines changed, ${replacedCount} replacements)`
|
|
757
|
+
);
|
|
758
|
+
return finish();
|
|
759
|
+
} catch (error) {
|
|
760
|
+
loader.fail("Failed to apply expand-object transform");
|
|
761
|
+
showError(
|
|
762
|
+
`Error transforming file '${filename}': ${error instanceof Error ? error.message : "Unknown error"}`
|
|
763
|
+
);
|
|
764
|
+
return finish();
|
|
765
|
+
}
|
|
766
|
+
} catch (error) {
|
|
767
|
+
showError(
|
|
768
|
+
`Error reading file '${filename}': ${error instanceof Error ? error.message : "Unknown error"}`
|
|
769
|
+
);
|
|
770
|
+
return finish();
|
|
771
|
+
}
|
|
772
|
+
},
|
|
773
|
+
options.unlimited ? null : 120 * 1e3
|
|
774
|
+
);
|
|
775
|
+
}
|
|
776
|
+
);
|
|
777
|
+
});
|
|
778
|
+
|
|
779
|
+
// commands/object-packer/index.ts
|
|
780
|
+
import { readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "fs";
|
|
781
|
+
import { basename as basename4, dirname as dirname4, extname as extname4, join as join4 } from "path";
|
|
782
|
+
import { parse as parse5 } from "@babel/parser";
|
|
783
|
+
import traverse4 from "@babel/traverse";
|
|
784
|
+
import generate4 from "@babel/generator";
|
|
785
|
+
import * as t3 from "@babel/types";
|
|
786
|
+
import loading5 from "loading-cli";
|
|
787
|
+
var createDefaultOutputPath4 = (inputPath) => {
|
|
788
|
+
const ext = extname4(inputPath);
|
|
789
|
+
if (!ext) {
|
|
790
|
+
return `${inputPath}.object-packer.js`;
|
|
791
|
+
}
|
|
792
|
+
const base = basename4(inputPath, ext);
|
|
793
|
+
return join4(dirname4(inputPath), `${base}.object-packer${ext}`);
|
|
794
|
+
};
|
|
795
|
+
var isEmptyObjectExpression = (node) => {
|
|
796
|
+
return t3.isObjectExpression(node) && node.properties.length === 0;
|
|
797
|
+
};
|
|
798
|
+
var isPropertyAssignment = (node, objectName) => {
|
|
799
|
+
return t3.isAssignmentExpression(node) && t3.isMemberExpression(node.left) && t3.isIdentifier(node.left.object, { name: objectName });
|
|
800
|
+
};
|
|
801
|
+
var hasSelfReference = (value, statementPath, arrayIndex, binding, log) => {
|
|
802
|
+
try {
|
|
803
|
+
const statementContainerPath = statementPath.parentPath?.get(
|
|
804
|
+
`${statementPath.parentKey}.${arrayIndex}`
|
|
805
|
+
);
|
|
806
|
+
let detected = false;
|
|
807
|
+
patchDefault(traverse4)(
|
|
808
|
+
value,
|
|
809
|
+
{
|
|
810
|
+
Identifier(path) {
|
|
811
|
+
if (detected) return;
|
|
812
|
+
if (path.node.name !== binding.identifier.name) return;
|
|
813
|
+
if (path.scope.getBinding(binding.identifier.name) === binding) {
|
|
814
|
+
detected = true;
|
|
815
|
+
path.stop();
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
},
|
|
819
|
+
statementContainerPath.scope,
|
|
820
|
+
void 0,
|
|
821
|
+
statementContainerPath
|
|
822
|
+
);
|
|
823
|
+
return detected;
|
|
824
|
+
} catch (error) {
|
|
825
|
+
log(
|
|
826
|
+
`Error looking for self reference when object packing: ${error instanceof Error ? error.message : String(error)}`
|
|
827
|
+
);
|
|
828
|
+
return false;
|
|
829
|
+
}
|
|
830
|
+
};
|
|
831
|
+
var packObjectProperties = (code, filename) => {
|
|
832
|
+
const ast = parse5(code, createParseOptions(filename));
|
|
833
|
+
let packedCount = 0;
|
|
834
|
+
let removedStatements = 0;
|
|
835
|
+
const log = (message) => console.warn(message);
|
|
836
|
+
patchDefault(traverse4)(ast, {
|
|
837
|
+
VariableDeclarator(path) {
|
|
838
|
+
if (!t3.isIdentifier(path.node.id)) return;
|
|
839
|
+
if (!path.node.init || !isEmptyObjectExpression(path.node.init)) return;
|
|
840
|
+
const binding = path.scope.getBinding(path.node.id.name);
|
|
841
|
+
if (!binding || !binding.constant) return;
|
|
842
|
+
const objectExpression = path.node.init;
|
|
843
|
+
const statementPath = path.getStatementParent();
|
|
844
|
+
if (!statementPath || !statementPath.parentPath || typeof statementPath.key !== "number") {
|
|
845
|
+
return;
|
|
846
|
+
}
|
|
847
|
+
const statements = statementPath.parentPath.node[statementPath.parentKey];
|
|
848
|
+
let localRemoved = 0;
|
|
849
|
+
let localPacked = 0;
|
|
850
|
+
for (let i = statementPath.key + 1; i < statements.length; i++) {
|
|
851
|
+
const node = statements[i];
|
|
852
|
+
if (t3.isExpressionStatement(node) && isPropertyAssignment(node.expression, path.node.id.name)) {
|
|
853
|
+
const assignment = node.expression;
|
|
854
|
+
if (isPropertyAssignment(assignment.right, path.node.id.name)) {
|
|
855
|
+
const properties = [assignment.left];
|
|
856
|
+
let right = assignment.right;
|
|
857
|
+
while (isPropertyAssignment(right, path.node.id.name)) {
|
|
858
|
+
properties.push(right.left);
|
|
859
|
+
right = right.right;
|
|
860
|
+
}
|
|
861
|
+
if (!t3.isLiteral(right)) {
|
|
862
|
+
break;
|
|
863
|
+
}
|
|
864
|
+
for (const { property } of properties) {
|
|
865
|
+
if (t3.isPrivateName(property)) {
|
|
866
|
+
break;
|
|
867
|
+
}
|
|
868
|
+
const isComputed = !t3.isStringLiteral(property) && !t3.isNumericLiteral(property) && !t3.isIdentifier(property);
|
|
869
|
+
objectExpression.properties.push(
|
|
870
|
+
t3.objectProperty(
|
|
871
|
+
t3.cloneNode(property),
|
|
872
|
+
t3.cloneNode(right, true),
|
|
873
|
+
isComputed
|
|
874
|
+
)
|
|
875
|
+
);
|
|
876
|
+
localPacked += 1;
|
|
877
|
+
}
|
|
878
|
+
localRemoved += 1;
|
|
879
|
+
} else {
|
|
880
|
+
const key = assignment.left.property;
|
|
881
|
+
if (t3.isPrivateName(key)) {
|
|
882
|
+
break;
|
|
883
|
+
}
|
|
884
|
+
const isComputed = !t3.isStringLiteral(key) && !t3.isNumericLiteral(key) && !t3.isIdentifier(key);
|
|
885
|
+
if (hasSelfReference(assignment.right, statementPath, i, binding, log)) {
|
|
886
|
+
break;
|
|
887
|
+
}
|
|
888
|
+
objectExpression.properties.push(
|
|
889
|
+
t3.objectProperty(
|
|
890
|
+
t3.cloneNode(key),
|
|
891
|
+
t3.cloneNode(assignment.right, true),
|
|
892
|
+
isComputed
|
|
893
|
+
)
|
|
894
|
+
);
|
|
895
|
+
localPacked += 1;
|
|
896
|
+
localRemoved += 1;
|
|
897
|
+
}
|
|
898
|
+
} else {
|
|
899
|
+
break;
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
if (localRemoved > 0) {
|
|
903
|
+
statements.splice(statementPath.key + 1, localRemoved);
|
|
904
|
+
packedCount += localPacked;
|
|
905
|
+
removedStatements += localRemoved;
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
});
|
|
909
|
+
return {
|
|
910
|
+
code: patchDefault(generate4)(ast).code,
|
|
911
|
+
packedCount,
|
|
912
|
+
removedStatements
|
|
913
|
+
};
|
|
914
|
+
};
|
|
915
|
+
var object_packer_default = createCommand((program2) => {
|
|
916
|
+
program2.command("object-packer").description("Pack consecutive object property assignments into literals").argument("[file]", "The file to transform").option("--input, --file <file>", "The file to transform").option("--o, --output <file>", "Output file path").option("--unlimited", "Unlimited timeout").action(
|
|
917
|
+
async (fileArgument, options) => {
|
|
918
|
+
await timeout(
|
|
919
|
+
async ({ finish }) => {
|
|
920
|
+
const filename = fileArgument ?? options.file ?? await createPrompt("Enter the file path:");
|
|
921
|
+
if (!filename) {
|
|
922
|
+
showError("No file provided");
|
|
923
|
+
return finish();
|
|
924
|
+
}
|
|
925
|
+
try {
|
|
926
|
+
const fileContent = readFileSync5(filename, "utf8");
|
|
927
|
+
const defaultOutputPath = createDefaultOutputPath4(filename);
|
|
928
|
+
let outputPath = options.output;
|
|
929
|
+
if (!outputPath) {
|
|
930
|
+
const promptPath = (await createPrompt("Enter the output file path:"))?.trim();
|
|
931
|
+
outputPath = promptPath || defaultOutputPath;
|
|
932
|
+
}
|
|
933
|
+
const loader = loading5("Packing object properties...").start();
|
|
934
|
+
try {
|
|
935
|
+
const { code: output, packedCount, removedStatements } = packObjectProperties(fileContent, filename);
|
|
936
|
+
writeFileSync4(outputPath, output, "utf8");
|
|
937
|
+
loader.succeed(
|
|
938
|
+
`Saved object-packer file to: ${outputPath} (${diff(fileContent, output).length} lines changed, ${packedCount} properties packed, ${removedStatements} statements removed)`
|
|
939
|
+
);
|
|
940
|
+
return finish();
|
|
941
|
+
} catch (error) {
|
|
942
|
+
loader.fail("Failed to apply object-packer transform");
|
|
943
|
+
showError(
|
|
944
|
+
`Error transforming file '${filename}': ${error instanceof Error ? error.message : "Unknown error"}`
|
|
945
|
+
);
|
|
946
|
+
return finish();
|
|
947
|
+
}
|
|
948
|
+
} catch (error) {
|
|
949
|
+
showError(
|
|
950
|
+
`Error reading file '${filename}': ${error instanceof Error ? error.message : "Unknown error"}`
|
|
951
|
+
);
|
|
952
|
+
return finish();
|
|
953
|
+
}
|
|
954
|
+
},
|
|
955
|
+
options.unlimited ? null : 120 * 1e3
|
|
956
|
+
);
|
|
957
|
+
}
|
|
958
|
+
);
|
|
959
|
+
});
|
|
960
|
+
|
|
961
|
+
// commands/pre-evaluate/index.ts
|
|
962
|
+
import { readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "fs";
|
|
963
|
+
import { basename as basename5, dirname as dirname5, extname as extname5, join as join5 } from "path";
|
|
964
|
+
import { parse as parse6 } from "@babel/parser";
|
|
965
|
+
import traverse5 from "@babel/traverse";
|
|
966
|
+
import generate5 from "@babel/generator";
|
|
967
|
+
import * as t4 from "@babel/types";
|
|
968
|
+
import loading6 from "loading-cli";
|
|
969
|
+
var createDefaultOutputPath5 = (inputPath) => {
|
|
970
|
+
const ext = extname5(inputPath);
|
|
971
|
+
if (!ext) {
|
|
972
|
+
return `${inputPath}.pre-evaluate.js`;
|
|
973
|
+
}
|
|
974
|
+
const base = basename5(inputPath, ext);
|
|
975
|
+
return join5(dirname5(inputPath), `${base}.pre-evaluate${ext}`);
|
|
509
976
|
};
|
|
510
977
|
var isSupportedNumberOperator = (operator) => {
|
|
511
978
|
return operator === "-" || operator === "*" || operator === "/" || operator === "%" || operator === "**" || operator === "<<" || operator === ">>" || operator === ">>>" || operator === "|" || operator === "&" || operator === "^";
|
|
@@ -639,13 +1106,13 @@ var shouldSkipReferencedIdentifier = (path) => {
|
|
|
639
1106
|
return false;
|
|
640
1107
|
};
|
|
641
1108
|
var preEvaluate = (code, filename) => {
|
|
642
|
-
const ast =
|
|
1109
|
+
const ast = parse6(code, createParseOptions(filename));
|
|
643
1110
|
const state = {
|
|
644
1111
|
bindingValues: /* @__PURE__ */ new Map(),
|
|
645
1112
|
bindingStack: /* @__PURE__ */ new Set()
|
|
646
1113
|
};
|
|
647
1114
|
let replacedCount = 0;
|
|
648
|
-
patchDefault(
|
|
1115
|
+
patchDefault(traverse5)(ast, {
|
|
649
1116
|
ReferencedIdentifier(path) {
|
|
650
1117
|
if (shouldSkipReferencedIdentifier(path)) {
|
|
651
1118
|
return;
|
|
@@ -653,9 +1120,9 @@ var preEvaluate = (code, filename) => {
|
|
|
653
1120
|
const value = evaluateExpression(path, state);
|
|
654
1121
|
if (value === null) return;
|
|
655
1122
|
if (typeof value === "number") {
|
|
656
|
-
path.replaceWith(
|
|
1123
|
+
path.replaceWith(t4.numericLiteral(value));
|
|
657
1124
|
} else {
|
|
658
|
-
path.replaceWith(
|
|
1125
|
+
path.replaceWith(t4.stringLiteral(value));
|
|
659
1126
|
}
|
|
660
1127
|
replacedCount += 1;
|
|
661
1128
|
},
|
|
@@ -664,9 +1131,9 @@ var preEvaluate = (code, filename) => {
|
|
|
664
1131
|
const value = evaluateExpression(path, state);
|
|
665
1132
|
if (value === null) return;
|
|
666
1133
|
if (typeof value === "number") {
|
|
667
|
-
path.replaceWith(
|
|
1134
|
+
path.replaceWith(t4.numericLiteral(value));
|
|
668
1135
|
} else {
|
|
669
|
-
path.replaceWith(
|
|
1136
|
+
path.replaceWith(t4.stringLiteral(value));
|
|
670
1137
|
}
|
|
671
1138
|
replacedCount += 1;
|
|
672
1139
|
}
|
|
@@ -676,9 +1143,9 @@ var preEvaluate = (code, filename) => {
|
|
|
676
1143
|
const value = evaluateExpression(path, state);
|
|
677
1144
|
if (value === null) return;
|
|
678
1145
|
if (typeof value === "number") {
|
|
679
|
-
path.replaceWith(
|
|
1146
|
+
path.replaceWith(t4.numericLiteral(value));
|
|
680
1147
|
} else {
|
|
681
|
-
path.replaceWith(
|
|
1148
|
+
path.replaceWith(t4.stringLiteral(value));
|
|
682
1149
|
}
|
|
683
1150
|
replacedCount += 1;
|
|
684
1151
|
}
|
|
@@ -688,16 +1155,16 @@ var preEvaluate = (code, filename) => {
|
|
|
688
1155
|
const value = evaluateExpression(path, state);
|
|
689
1156
|
if (value === null) return;
|
|
690
1157
|
if (typeof value === "number") {
|
|
691
|
-
path.replaceWith(
|
|
1158
|
+
path.replaceWith(t4.numericLiteral(value));
|
|
692
1159
|
} else {
|
|
693
|
-
path.replaceWith(
|
|
1160
|
+
path.replaceWith(t4.stringLiteral(value));
|
|
694
1161
|
}
|
|
695
1162
|
replacedCount += 1;
|
|
696
1163
|
}
|
|
697
1164
|
}
|
|
698
1165
|
});
|
|
699
1166
|
return {
|
|
700
|
-
code: patchDefault(
|
|
1167
|
+
code: patchDefault(generate5)(ast).code,
|
|
701
1168
|
replacedCount
|
|
702
1169
|
};
|
|
703
1170
|
};
|
|
@@ -712,20 +1179,20 @@ var pre_evaluate_default = createCommand((program2) => {
|
|
|
712
1179
|
return finish();
|
|
713
1180
|
}
|
|
714
1181
|
try {
|
|
715
|
-
const fileContent =
|
|
716
|
-
const defaultOutputPath =
|
|
1182
|
+
const fileContent = readFileSync6(filename, "utf8");
|
|
1183
|
+
const defaultOutputPath = createDefaultOutputPath5(filename);
|
|
717
1184
|
let outputPath = options.output;
|
|
718
1185
|
if (!outputPath) {
|
|
719
1186
|
const promptPath = (await createPrompt("Enter the output file path:"))?.trim();
|
|
720
1187
|
outputPath = promptPath || defaultOutputPath;
|
|
721
1188
|
}
|
|
722
|
-
const loader =
|
|
1189
|
+
const loader = loading6("Pre-evaluating constants...").start();
|
|
723
1190
|
try {
|
|
724
1191
|
const { code: output, replacedCount } = preEvaluate(
|
|
725
1192
|
fileContent,
|
|
726
1193
|
filename
|
|
727
1194
|
);
|
|
728
|
-
|
|
1195
|
+
writeFileSync5(outputPath, output, "utf8");
|
|
729
1196
|
loader.succeed(
|
|
730
1197
|
`Saved pre-evaluate file to: ${outputPath} (${diff(fileContent, output).length} lines changed, ${replacedCount} replacements)`
|
|
731
1198
|
);
|
|
@@ -750,6 +1217,112 @@ var pre_evaluate_default = createCommand((program2) => {
|
|
|
750
1217
|
);
|
|
751
1218
|
});
|
|
752
1219
|
|
|
1220
|
+
// commands/remove-unused/index.ts
|
|
1221
|
+
import { readFileSync as readFileSync7, writeFileSync as writeFileSync6 } from "fs";
|
|
1222
|
+
import { basename as basename6, dirname as dirname6, extname as extname6, join as join6 } from "path";
|
|
1223
|
+
import { parse as parse7 } from "@babel/parser";
|
|
1224
|
+
import traverse6 from "@babel/traverse";
|
|
1225
|
+
import generate6 from "@babel/generator";
|
|
1226
|
+
import * as t5 from "@babel/types";
|
|
1227
|
+
import loading7 from "loading-cli";
|
|
1228
|
+
var createDefaultOutputPath6 = (inputPath) => {
|
|
1229
|
+
const ext = extname6(inputPath);
|
|
1230
|
+
if (!ext) {
|
|
1231
|
+
return `${inputPath}.remove-unused.js`;
|
|
1232
|
+
}
|
|
1233
|
+
const base = basename6(inputPath, ext);
|
|
1234
|
+
return join6(dirname6(inputPath), `${base}.remove-unused${ext}`);
|
|
1235
|
+
};
|
|
1236
|
+
var removeUnusedVariables = (code, filename) => {
|
|
1237
|
+
const ast = parse7(code, createParseOptions(filename));
|
|
1238
|
+
let changed = false;
|
|
1239
|
+
patchDefault(traverse6)(ast, {
|
|
1240
|
+
Scope(path) {
|
|
1241
|
+
for (const binding of Object.values(path.scope.bindings)) {
|
|
1242
|
+
if (!binding.referenced && binding.constantViolations.length === 0 && binding.path.key !== "handler" && !binding.path.isFunctionExpression()) {
|
|
1243
|
+
if (t5.isProgram(binding.scope.block) && (binding.kind === "var" || binding.kind === "hoisted")) {
|
|
1244
|
+
continue;
|
|
1245
|
+
}
|
|
1246
|
+
const targets = binding.path.parentKey === "params" ? [...binding.referencePaths, ...binding.constantViolations] : [
|
|
1247
|
+
binding.path,
|
|
1248
|
+
...binding.referencePaths,
|
|
1249
|
+
...binding.constantViolations
|
|
1250
|
+
];
|
|
1251
|
+
for (const targetPath of targets) {
|
|
1252
|
+
if (targetPath.isVariableDeclarator() && (t5.isArrayPattern(targetPath.node.id) && targetPath.node.id.elements.length > 1 || t5.isObjectPattern(targetPath.node.id) && targetPath.node.id.properties.length > 1)) {
|
|
1253
|
+
continue;
|
|
1254
|
+
}
|
|
1255
|
+
if (targetPath.key === "consequent" || targetPath.key === "alternate" || targetPath.key === "body") {
|
|
1256
|
+
targetPath.replaceWith(t5.blockStatement([]));
|
|
1257
|
+
} else {
|
|
1258
|
+
const parentPath = targetPath.parentPath;
|
|
1259
|
+
if (parentPath?.isVariableDeclaration() && parentPath.node.declarations.length === 1) {
|
|
1260
|
+
parentPath.remove();
|
|
1261
|
+
} else {
|
|
1262
|
+
targetPath.remove();
|
|
1263
|
+
}
|
|
1264
|
+
}
|
|
1265
|
+
changed = true;
|
|
1266
|
+
}
|
|
1267
|
+
}
|
|
1268
|
+
}
|
|
1269
|
+
}
|
|
1270
|
+
});
|
|
1271
|
+
return {
|
|
1272
|
+
code: patchDefault(generate6)(ast).code,
|
|
1273
|
+
changed
|
|
1274
|
+
};
|
|
1275
|
+
};
|
|
1276
|
+
var remove_unused_default = createCommand((program2) => {
|
|
1277
|
+
program2.command("remove-unused").description("Remove unused variables and declarations").argument("[file]", "The file to transform").option("--input, --file <file>", "The file to transform").option("--o, --output <file>", "Output file path").option("--unlimited", "Unlimited timeout").action(
|
|
1278
|
+
async (fileArgument, options) => {
|
|
1279
|
+
await timeout(
|
|
1280
|
+
async ({ finish }) => {
|
|
1281
|
+
const filename = fileArgument ?? options.file ?? await createPrompt("Enter the file path:");
|
|
1282
|
+
if (!filename) {
|
|
1283
|
+
showError("No file provided");
|
|
1284
|
+
return finish();
|
|
1285
|
+
}
|
|
1286
|
+
try {
|
|
1287
|
+
const fileContent = readFileSync7(filename, "utf8");
|
|
1288
|
+
const defaultOutputPath = createDefaultOutputPath6(filename);
|
|
1289
|
+
let outputPath = options.output;
|
|
1290
|
+
if (!outputPath) {
|
|
1291
|
+
const promptPath = (await createPrompt("Enter the output file path:"))?.trim();
|
|
1292
|
+
outputPath = promptPath || defaultOutputPath;
|
|
1293
|
+
}
|
|
1294
|
+
const loader = loading7("Removing unused variables...").start();
|
|
1295
|
+
try {
|
|
1296
|
+
const { code: output, changed } = removeUnusedVariables(
|
|
1297
|
+
fileContent,
|
|
1298
|
+
filename
|
|
1299
|
+
);
|
|
1300
|
+
writeFileSync6(outputPath, output, "utf8");
|
|
1301
|
+
const diffLines = diff(fileContent, output).length;
|
|
1302
|
+
loader.succeed(
|
|
1303
|
+
`Saved remove-unused file to: ${outputPath} (${diffLines} lines changed${changed ? ", removed unused declarations" : ", no changes"})`
|
|
1304
|
+
);
|
|
1305
|
+
return finish();
|
|
1306
|
+
} catch (error) {
|
|
1307
|
+
loader.fail("Failed to apply remove-unused transform");
|
|
1308
|
+
showError(
|
|
1309
|
+
`Error transforming file '${filename}': ${error instanceof Error ? error.message : "Unknown error"}`
|
|
1310
|
+
);
|
|
1311
|
+
return finish();
|
|
1312
|
+
}
|
|
1313
|
+
} catch (error) {
|
|
1314
|
+
showError(
|
|
1315
|
+
`Error reading file '${filename}': ${error instanceof Error ? error.message : "Unknown error"}`
|
|
1316
|
+
);
|
|
1317
|
+
return finish();
|
|
1318
|
+
}
|
|
1319
|
+
},
|
|
1320
|
+
options.unlimited ? null : 120 * 1e3
|
|
1321
|
+
);
|
|
1322
|
+
}
|
|
1323
|
+
);
|
|
1324
|
+
});
|
|
1325
|
+
|
|
753
1326
|
// utils/cli/showCredit.ts
|
|
754
1327
|
import chalk3 from "chalk";
|
|
755
1328
|
var beautify = (strings, ...values) => {
|
|
@@ -774,10 +1347,10 @@ var calmGradienrain = (text) => {
|
|
|
774
1347
|
const endHue = 300;
|
|
775
1348
|
const saturation = 0.45;
|
|
776
1349
|
const value = 0.8;
|
|
777
|
-
const ease = (
|
|
1350
|
+
const ease = (t6) => t6 * t6 * (3 - 2 * t6);
|
|
778
1351
|
return text.split("").map((char, i) => {
|
|
779
|
-
const
|
|
780
|
-
const hue = startHue + (endHue - startHue) *
|
|
1352
|
+
const t6 = ease(i / Math.max(text.length - 1, 1));
|
|
1353
|
+
const hue = startHue + (endHue - startHue) * t6;
|
|
781
1354
|
const c = value * saturation;
|
|
782
1355
|
const h = hue / 60;
|
|
783
1356
|
const x = c * (1 - Math.abs(h % 2 - 1));
|
|
@@ -801,10 +1374,10 @@ ${calmGradienrain(`Expose Kit v${VERSION}`)}
|
|
|
801
1374
|
`;
|
|
802
1375
|
|
|
803
1376
|
// index.ts
|
|
804
|
-
import { readFileSync as
|
|
1377
|
+
import { readFileSync as readFileSync8 } from "fs";
|
|
805
1378
|
var __filename = fileURLToPath(import.meta.url);
|
|
806
|
-
var __dirname =
|
|
807
|
-
var pkg = JSON.parse(
|
|
1379
|
+
var __dirname = dirname7(__filename);
|
|
1380
|
+
var pkg = JSON.parse(readFileSync8(join7(__dirname, "package.json"), "utf8"));
|
|
808
1381
|
console.log(showCredit(pkg.version));
|
|
809
1382
|
console.log();
|
|
810
1383
|
var program = new Command();
|
|
@@ -813,7 +1386,15 @@ program.name("expose").description("CLI for Deobfuscating").version(
|
|
|
813
1386
|
"-v, --version",
|
|
814
1387
|
"display version number"
|
|
815
1388
|
);
|
|
816
|
-
var commands = [
|
|
1389
|
+
var commands = [
|
|
1390
|
+
parsable_default,
|
|
1391
|
+
safe_scope_default,
|
|
1392
|
+
expand_array_default,
|
|
1393
|
+
expand_object_default,
|
|
1394
|
+
object_packer_default,
|
|
1395
|
+
pre_evaluate_default,
|
|
1396
|
+
remove_unused_default
|
|
1397
|
+
];
|
|
817
1398
|
for (const command of commands) {
|
|
818
1399
|
command(program);
|
|
819
1400
|
}
|
package/dist/package.json
CHANGED