expose-kit 0.4.0 → 0.5.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.
@@ -175,7 +175,7 @@ Args:
175
175
 
176
176
  ---
177
177
 
178
- ### `expose expand-array`
178
+ ### `expose expand-array`
179
179
 
180
180
  Expand array index access for primitive values.
181
181
  ```js
@@ -196,13 +196,60 @@ 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
- Notes:
203
- - Each replacement is validated by reparsing; invalid replacements (e.g. `++a[0]` or `a[0]++`) are skipped.-
200
+ Notes:
201
+ - Each replacement is validated by reparsing; invalid replacements (e.g. `++a[0]` or `a[0]++`) are skipped.-
202
+
203
+ ---
204
+
205
+ ### `expose expand-object`
206
+
207
+ Expand object property access for primitive values.
208
+ ```js
209
+ const obj = { a: 1, b: 2 };
210
+ // before
211
+ console.log(obj.a, obj["b"]);
212
+ // after
213
+ console.log(1, 2);
214
+ ```
215
+ Example is [here](https://github.com/evex-dev/expose-kit/tree/main/commands/expand-object/mocks).
216
+
217
+ ```bash
218
+ expose expand-object path/to/file.js --target objectName --output path/to/file.expand-object.js
219
+ ```
220
+
221
+ Args:
222
+ - `--target <name>`
223
+ Target object variable name
224
+ - `--o, --output <file>`
225
+ Output file path
226
+
227
+ Notes:
228
+ - Each replacement is validated by reparsing; invalid replacements are skipped.
229
+
230
+ ---
231
+
232
+ ### `expose remove-unused`
233
+
234
+ Remove unused variabless.
235
+ ```js
236
+ // before
237
+ var a = 0, b = 1;
238
+ console.log(a);
239
+ // after
240
+ var a = 0;
241
+ console.log(a);
242
+ ```
243
+ Example is [here](https://github.com/evex-dev/expose-kit/tree/main/commands/remove-unused/mocks).
244
+
245
+ ```bash
246
+ expose remove-unused path/to/file.js --output path/to/file.remove-unused.js
247
+ ```
248
+
249
+ Args:
250
+ - `--o, --output <file>`
251
+ Output file path
204
252
 
205
- ---
206
253
 
207
254
  ## Community & Support
208
255
 
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 dirname6, join as join6 } 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,281 @@ 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/pre-evaluate/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}.pre-evaluate.js`;
791
+ }
792
+ const base = basename4(inputPath, ext);
793
+ return join4(dirname4(inputPath), `${base}.pre-evaluate${ext}`);
509
794
  };
510
795
  var isSupportedNumberOperator = (operator) => {
511
796
  return operator === "-" || operator === "*" || operator === "/" || operator === "%" || operator === "**" || operator === "<<" || operator === ">>" || operator === ">>>" || operator === "|" || operator === "&" || operator === "^";
@@ -639,13 +924,13 @@ var shouldSkipReferencedIdentifier = (path) => {
639
924
  return false;
640
925
  };
641
926
  var preEvaluate = (code, filename) => {
642
- const ast = parse4(code, createParseOptions(filename));
927
+ const ast = parse5(code, createParseOptions(filename));
643
928
  const state = {
644
929
  bindingValues: /* @__PURE__ */ new Map(),
645
930
  bindingStack: /* @__PURE__ */ new Set()
646
931
  };
647
932
  let replacedCount = 0;
648
- patchDefault(traverse3)(ast, {
933
+ patchDefault(traverse4)(ast, {
649
934
  ReferencedIdentifier(path) {
650
935
  if (shouldSkipReferencedIdentifier(path)) {
651
936
  return;
@@ -653,9 +938,9 @@ var preEvaluate = (code, filename) => {
653
938
  const value = evaluateExpression(path, state);
654
939
  if (value === null) return;
655
940
  if (typeof value === "number") {
656
- path.replaceWith(t2.numericLiteral(value));
941
+ path.replaceWith(t3.numericLiteral(value));
657
942
  } else {
658
- path.replaceWith(t2.stringLiteral(value));
943
+ path.replaceWith(t3.stringLiteral(value));
659
944
  }
660
945
  replacedCount += 1;
661
946
  },
@@ -664,9 +949,9 @@ var preEvaluate = (code, filename) => {
664
949
  const value = evaluateExpression(path, state);
665
950
  if (value === null) return;
666
951
  if (typeof value === "number") {
667
- path.replaceWith(t2.numericLiteral(value));
952
+ path.replaceWith(t3.numericLiteral(value));
668
953
  } else {
669
- path.replaceWith(t2.stringLiteral(value));
954
+ path.replaceWith(t3.stringLiteral(value));
670
955
  }
671
956
  replacedCount += 1;
672
957
  }
@@ -676,9 +961,9 @@ var preEvaluate = (code, filename) => {
676
961
  const value = evaluateExpression(path, state);
677
962
  if (value === null) return;
678
963
  if (typeof value === "number") {
679
- path.replaceWith(t2.numericLiteral(value));
964
+ path.replaceWith(t3.numericLiteral(value));
680
965
  } else {
681
- path.replaceWith(t2.stringLiteral(value));
966
+ path.replaceWith(t3.stringLiteral(value));
682
967
  }
683
968
  replacedCount += 1;
684
969
  }
@@ -688,16 +973,16 @@ var preEvaluate = (code, filename) => {
688
973
  const value = evaluateExpression(path, state);
689
974
  if (value === null) return;
690
975
  if (typeof value === "number") {
691
- path.replaceWith(t2.numericLiteral(value));
976
+ path.replaceWith(t3.numericLiteral(value));
692
977
  } else {
693
- path.replaceWith(t2.stringLiteral(value));
978
+ path.replaceWith(t3.stringLiteral(value));
694
979
  }
695
980
  replacedCount += 1;
696
981
  }
697
982
  }
698
983
  });
699
984
  return {
700
- code: patchDefault(generate3)(ast).code,
985
+ code: patchDefault(generate4)(ast).code,
701
986
  replacedCount
702
987
  };
703
988
  };
@@ -712,20 +997,20 @@ var pre_evaluate_default = createCommand((program2) => {
712
997
  return finish();
713
998
  }
714
999
  try {
715
- const fileContent = readFileSync4(filename, "utf8");
716
- const defaultOutputPath = createDefaultOutputPath3(filename);
1000
+ const fileContent = readFileSync5(filename, "utf8");
1001
+ const defaultOutputPath = createDefaultOutputPath4(filename);
717
1002
  let outputPath = options.output;
718
1003
  if (!outputPath) {
719
1004
  const promptPath = (await createPrompt("Enter the output file path:"))?.trim();
720
1005
  outputPath = promptPath || defaultOutputPath;
721
1006
  }
722
- const loader = loading4("Pre-evaluating constants...").start();
1007
+ const loader = loading5("Pre-evaluating constants...").start();
723
1008
  try {
724
1009
  const { code: output, replacedCount } = preEvaluate(
725
1010
  fileContent,
726
1011
  filename
727
1012
  );
728
- writeFileSync3(outputPath, output, "utf8");
1013
+ writeFileSync4(outputPath, output, "utf8");
729
1014
  loader.succeed(
730
1015
  `Saved pre-evaluate file to: ${outputPath} (${diff(fileContent, output).length} lines changed, ${replacedCount} replacements)`
731
1016
  );
@@ -750,6 +1035,112 @@ var pre_evaluate_default = createCommand((program2) => {
750
1035
  );
751
1036
  });
752
1037
 
1038
+ // commands/remove-unused/index.ts
1039
+ import { readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "fs";
1040
+ import { basename as basename5, dirname as dirname5, extname as extname5, join as join5 } from "path";
1041
+ import { parse as parse6 } from "@babel/parser";
1042
+ import traverse5 from "@babel/traverse";
1043
+ import generate5 from "@babel/generator";
1044
+ import * as t4 from "@babel/types";
1045
+ import loading6 from "loading-cli";
1046
+ var createDefaultOutputPath5 = (inputPath) => {
1047
+ const ext = extname5(inputPath);
1048
+ if (!ext) {
1049
+ return `${inputPath}.remove-unused.js`;
1050
+ }
1051
+ const base = basename5(inputPath, ext);
1052
+ return join5(dirname5(inputPath), `${base}.remove-unused${ext}`);
1053
+ };
1054
+ var removeUnusedVariables = (code, filename) => {
1055
+ const ast = parse6(code, createParseOptions(filename));
1056
+ let changed = false;
1057
+ patchDefault(traverse5)(ast, {
1058
+ Scope(path) {
1059
+ for (const binding of Object.values(path.scope.bindings)) {
1060
+ if (!binding.referenced && binding.constantViolations.length === 0 && binding.path.key !== "handler" && !binding.path.isFunctionExpression()) {
1061
+ if (t4.isProgram(binding.scope.block) && (binding.kind === "var" || binding.kind === "hoisted")) {
1062
+ continue;
1063
+ }
1064
+ const targets = binding.path.parentKey === "params" ? [...binding.referencePaths, ...binding.constantViolations] : [
1065
+ binding.path,
1066
+ ...binding.referencePaths,
1067
+ ...binding.constantViolations
1068
+ ];
1069
+ for (const targetPath of targets) {
1070
+ if (targetPath.isVariableDeclarator() && (t4.isArrayPattern(targetPath.node.id) && targetPath.node.id.elements.length > 1 || t4.isObjectPattern(targetPath.node.id) && targetPath.node.id.properties.length > 1)) {
1071
+ continue;
1072
+ }
1073
+ if (targetPath.key === "consequent" || targetPath.key === "alternate" || targetPath.key === "body") {
1074
+ targetPath.replaceWith(t4.blockStatement([]));
1075
+ } else {
1076
+ const parentPath = targetPath.parentPath;
1077
+ if (parentPath?.isVariableDeclaration() && parentPath.node.declarations.length === 1) {
1078
+ parentPath.remove();
1079
+ } else {
1080
+ targetPath.remove();
1081
+ }
1082
+ }
1083
+ changed = true;
1084
+ }
1085
+ }
1086
+ }
1087
+ }
1088
+ });
1089
+ return {
1090
+ code: patchDefault(generate5)(ast).code,
1091
+ changed
1092
+ };
1093
+ };
1094
+ var remove_unused_default = createCommand((program2) => {
1095
+ 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(
1096
+ async (fileArgument, options) => {
1097
+ await timeout(
1098
+ async ({ finish }) => {
1099
+ const filename = fileArgument ?? options.file ?? await createPrompt("Enter the file path:");
1100
+ if (!filename) {
1101
+ showError("No file provided");
1102
+ return finish();
1103
+ }
1104
+ try {
1105
+ const fileContent = readFileSync6(filename, "utf8");
1106
+ const defaultOutputPath = createDefaultOutputPath5(filename);
1107
+ let outputPath = options.output;
1108
+ if (!outputPath) {
1109
+ const promptPath = (await createPrompt("Enter the output file path:"))?.trim();
1110
+ outputPath = promptPath || defaultOutputPath;
1111
+ }
1112
+ const loader = loading6("Removing unused variables...").start();
1113
+ try {
1114
+ const { code: output, changed } = removeUnusedVariables(
1115
+ fileContent,
1116
+ filename
1117
+ );
1118
+ writeFileSync5(outputPath, output, "utf8");
1119
+ const diffLines = diff(fileContent, output).length;
1120
+ loader.succeed(
1121
+ `Saved remove-unused file to: ${outputPath} (${diffLines} lines changed${changed ? ", removed unused declarations" : ", no changes"})`
1122
+ );
1123
+ return finish();
1124
+ } catch (error) {
1125
+ loader.fail("Failed to apply remove-unused transform");
1126
+ showError(
1127
+ `Error transforming file '${filename}': ${error instanceof Error ? error.message : "Unknown error"}`
1128
+ );
1129
+ return finish();
1130
+ }
1131
+ } catch (error) {
1132
+ showError(
1133
+ `Error reading file '${filename}': ${error instanceof Error ? error.message : "Unknown error"}`
1134
+ );
1135
+ return finish();
1136
+ }
1137
+ },
1138
+ options.unlimited ? null : 120 * 1e3
1139
+ );
1140
+ }
1141
+ );
1142
+ });
1143
+
753
1144
  // utils/cli/showCredit.ts
754
1145
  import chalk3 from "chalk";
755
1146
  var beautify = (strings, ...values) => {
@@ -774,10 +1165,10 @@ var calmGradienrain = (text) => {
774
1165
  const endHue = 300;
775
1166
  const saturation = 0.45;
776
1167
  const value = 0.8;
777
- const ease = (t3) => t3 * t3 * (3 - 2 * t3);
1168
+ const ease = (t5) => t5 * t5 * (3 - 2 * t5);
778
1169
  return text.split("").map((char, i) => {
779
- const t3 = ease(i / Math.max(text.length - 1, 1));
780
- const hue = startHue + (endHue - startHue) * t3;
1170
+ const t5 = ease(i / Math.max(text.length - 1, 1));
1171
+ const hue = startHue + (endHue - startHue) * t5;
781
1172
  const c = value * saturation;
782
1173
  const h = hue / 60;
783
1174
  const x = c * (1 - Math.abs(h % 2 - 1));
@@ -801,10 +1192,10 @@ ${calmGradienrain(`Expose Kit v${VERSION}`)}
801
1192
  `;
802
1193
 
803
1194
  // index.ts
804
- import { readFileSync as readFileSync5 } from "fs";
1195
+ import { readFileSync as readFileSync7 } from "fs";
805
1196
  var __filename = fileURLToPath(import.meta.url);
806
- var __dirname = dirname4(__filename);
807
- var pkg = JSON.parse(readFileSync5(join4(__dirname, "package.json"), "utf8"));
1197
+ var __dirname = dirname6(__filename);
1198
+ var pkg = JSON.parse(readFileSync7(join6(__dirname, "package.json"), "utf8"));
808
1199
  console.log(showCredit(pkg.version));
809
1200
  console.log();
810
1201
  var program = new Command();
@@ -813,7 +1204,14 @@ program.name("expose").description("CLI for Deobfuscating").version(
813
1204
  "-v, --version",
814
1205
  "display version number"
815
1206
  );
816
- var commands = [parsable_default, safe_scope_default, expand_array_default, pre_evaluate_default];
1207
+ var commands = [
1208
+ parsable_default,
1209
+ safe_scope_default,
1210
+ expand_array_default,
1211
+ expand_object_default,
1212
+ pre_evaluate_default,
1213
+ remove_unused_default
1214
+ ];
817
1215
  for (const command of commands) {
818
1216
  command(program);
819
1217
  }
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expose-kit",
3
- "version": "0.4.0",
3
+ "version": "0.5.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.5.0",
4
4
  "type": "module",
5
5
  "private": false,
6
6
  "author": "EdamAmex <edame8080@gmail.com> (https://github.com/EdamAme-x)",