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 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; invalid replacements (e.g. `++a[0]` or `a[0]++`) are skipped.-
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 dirname4, join as join4 } from "path";
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
- Identifier(path) {
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 (parent.isVariableDeclarator() && parent.get("id") === path) return;
373
- if (parent.isAssignmentExpression() && parent.get("left") === path) return;
374
- if (parent.isMemberExpression() && parent.get("object") === path) return;
375
- hasRiskOfSideEffects = true;
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("The target array has risk of side effects, do you want to continue? (y/n)");
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/pre-evaluate/index.ts
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}.pre-evaluate.js`;
519
+ return `${inputPath}.expand-object.js`;
506
520
  }
507
521
  const base = basename3(inputPath, ext);
508
- return join3(dirname3(inputPath), `${base}.pre-evaluate${ext}`);
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 = parse4(code, createParseOptions(filename));
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(traverse3)(ast, {
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(t2.numericLiteral(value));
1123
+ path.replaceWith(t4.numericLiteral(value));
657
1124
  } else {
658
- path.replaceWith(t2.stringLiteral(value));
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(t2.numericLiteral(value));
1134
+ path.replaceWith(t4.numericLiteral(value));
668
1135
  } else {
669
- path.replaceWith(t2.stringLiteral(value));
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(t2.numericLiteral(value));
1146
+ path.replaceWith(t4.numericLiteral(value));
680
1147
  } else {
681
- path.replaceWith(t2.stringLiteral(value));
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(t2.numericLiteral(value));
1158
+ path.replaceWith(t4.numericLiteral(value));
692
1159
  } else {
693
- path.replaceWith(t2.stringLiteral(value));
1160
+ path.replaceWith(t4.stringLiteral(value));
694
1161
  }
695
1162
  replacedCount += 1;
696
1163
  }
697
1164
  }
698
1165
  });
699
1166
  return {
700
- code: patchDefault(generate3)(ast).code,
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 = readFileSync4(filename, "utf8");
716
- const defaultOutputPath = createDefaultOutputPath3(filename);
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 = loading4("Pre-evaluating constants...").start();
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
- writeFileSync3(outputPath, output, "utf8");
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 = (t3) => t3 * t3 * (3 - 2 * t3);
1350
+ const ease = (t6) => t6 * t6 * (3 - 2 * t6);
778
1351
  return text.split("").map((char, i) => {
779
- const t3 = ease(i / Math.max(text.length - 1, 1));
780
- const hue = startHue + (endHue - startHue) * t3;
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 readFileSync5 } from "fs";
1377
+ import { readFileSync as readFileSync8 } from "fs";
805
1378
  var __filename = fileURLToPath(import.meta.url);
806
- var __dirname = dirname4(__filename);
807
- var pkg = JSON.parse(readFileSync5(join4(__dirname, "package.json"), "utf8"));
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 = [parsable_default, safe_scope_default, expand_array_default, pre_evaluate_default];
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expose-kit",
3
- "version": "0.4.0",
3
+ "version": "0.6.0",
4
4
  "type": "module",
5
5
  "private": false,
6
6
  "author": "EdamAmex <edame8080@gmail.com> (https://github.com/EdamAme-x)",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expose-kit",
3
- "version": "0.4.0",
3
+ "version": "0.6.0",
4
4
  "type": "module",
5
5
  "private": false,
6
6
  "author": "EdamAmex <edame8080@gmail.com> (https://github.com/EdamAme-x)",