expose-kit 0.3.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.
@@ -124,7 +124,7 @@ Check whether a file is syntactically valid.
124
124
 
125
125
  ```js
126
126
  parsable: const x = 810;
127
- not parsable: cons x; = 810;
127
+ not parsable: const ;x; == 810;
128
128
  ```
129
129
 
130
130
  ```bash
@@ -143,6 +143,7 @@ Rename bindings per scope for safer transformations.
143
143
  ```bash
144
144
  expose safe-scope path/to/file.js --output path/to/file.safe-scope.js
145
145
  ```
146
+ Example is [here](https://github.com/evex-dev/expose-kit/tree/main/commands/safe-scope/mocks).
146
147
 
147
148
  Args:
148
149
  - `--o, --output <file>`
@@ -152,6 +153,104 @@ Args:
152
153
 
153
154
  ---
154
155
 
156
+ ### `expose pre-evaluate`
157
+
158
+ Pre-evaluate const numeric/string expressions. (Safe evaluate)
159
+
160
+ ```js
161
+ const a = 1 + 2 * 3; // => 7
162
+ const c = "aaaa";
163
+ const b = "a" + c; // => "aaaaa"
164
+ ```
165
+
166
+ ```bash
167
+ expose pre-evaluate path/to/file.js --output path/to/file.pre-evaluate.js
168
+ ```
169
+
170
+ Args:
171
+ - `--o, --output <file>`
172
+ Output file path
173
+ - No extension → `file.pre-evaluate.js`
174
+ - With extension → `file.pre-evaluate.<ext>`
175
+
176
+ ---
177
+
178
+ ### `expose expand-array`
179
+
180
+ Expand array index access for primitive values.
181
+ ```js
182
+ var a = [1, 1, 4, 5, 1, 4];
183
+ // before
184
+ console.log(a[0], a[2], a[3]);
185
+ // after
186
+ console.log(1, 4, 5);
187
+ ```
188
+ Example is [here](https://github.com/evex-dev/expose-kit/tree/main/commands/expand-array/mocks).
189
+
190
+ ```bash
191
+ expose expand-array path/to/file.js --target arrayName --output path/to/file.expand-array.js
192
+ ```
193
+
194
+ Args:
195
+ - `--target <name>`
196
+ Target array variable name
197
+ - `--o, --output <file>`
198
+ Output file path
199
+
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
252
+
253
+
155
254
  ## Community & Support
156
255
 
157
256
  - 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 dirname2, join as join2 } 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
 
@@ -261,6 +261,886 @@ var safe_scope_default = createCommand((program2) => {
261
261
  );
262
262
  });
263
263
 
264
+ // commands/expand-array/index.ts
265
+ import { readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
266
+ import { basename as basename2, dirname as dirname2, extname as extname2, join as join2 } from "path";
267
+ import { parse as parse3 } from "@babel/parser";
268
+ import traverse2 from "@babel/traverse";
269
+ import generate2 from "@babel/generator";
270
+ import * as t from "@babel/types";
271
+ import loading3 from "loading-cli";
272
+ var createDefaultOutputPath2 = (inputPath) => {
273
+ const ext = extname2(inputPath);
274
+ if (!ext) {
275
+ return `${inputPath}.expand-array.js`;
276
+ }
277
+ const base = basename2(inputPath, ext);
278
+ return join2(dirname2(inputPath), `${base}.expand-array${ext}`);
279
+ };
280
+ var isPrimitiveArray = (arrayNode) => {
281
+ return arrayNode.elements.every((element) => {
282
+ if (!element || t.isSpreadElement(element)) {
283
+ return false;
284
+ }
285
+ return true;
286
+ });
287
+ };
288
+ var getIndexFromProperty = (property) => {
289
+ if (t.isNumericLiteral(property) && Number.isInteger(property.value)) {
290
+ return property.value;
291
+ }
292
+ if (t.isStringLiteral(property)) {
293
+ if (!/^-?\d+$/.test(property.value)) {
294
+ return null;
295
+ }
296
+ return Number.parseInt(property.value, 10);
297
+ }
298
+ if (t.isUnaryExpression(property, { operator: "-" }) && t.isNumericLiteral(property.argument)) {
299
+ return -property.argument.value;
300
+ }
301
+ return null;
302
+ };
303
+ var isAssignmentTarget = (path) => {
304
+ const parent = path.parentPath;
305
+ if (!parent) return false;
306
+ if (parent.isUpdateExpression()) return true;
307
+ if (parent.isAssignmentExpression() && parent.get("left") === path) {
308
+ return true;
309
+ }
310
+ if (parent.isForInStatement() && parent.get("left") === path) {
311
+ return true;
312
+ }
313
+ if (parent.isForOfStatement() && parent.get("left") === path) {
314
+ return true;
315
+ }
316
+ return false;
317
+ };
318
+ var collectMutatedIndexes = (ast, targetName) => {
319
+ const mutatedIndexes = /* @__PURE__ */ new Set();
320
+ let hasUnknownMutations = false;
321
+ patchDefault(traverse2)(ast, {
322
+ MemberExpression(path) {
323
+ if (!path.node.computed) return;
324
+ if (!t.isIdentifier(path.node.object, { name: targetName })) return;
325
+ const parent = path.parentPath;
326
+ if (!parent) return;
327
+ const isMutationTarget = parent.isUpdateExpression() || parent.isAssignmentExpression() && parent.get("left") === path || parent.isForInStatement() && parent.get("left") === path || parent.isForOfStatement() && parent.get("left") === path;
328
+ if (!isMutationTarget) return;
329
+ if (!t.isExpression(path.node.property)) return;
330
+ const index = getIndexFromProperty(path.node.property);
331
+ if (index === null) {
332
+ hasUnknownMutations = true;
333
+ return;
334
+ }
335
+ mutatedIndexes.add(index);
336
+ }
337
+ });
338
+ return { mutatedIndexes, hasUnknownMutations };
339
+ };
340
+ var findTargetArray = (ast, targetName) => {
341
+ let found = null;
342
+ patchDefault(traverse2)(ast, {
343
+ VariableDeclarator(path) {
344
+ if (found) return;
345
+ if (!t.isIdentifier(path.node.id, { name: targetName })) return;
346
+ if (!t.isArrayExpression(path.node.init)) return;
347
+ if (!isPrimitiveArray(path.node.init)) return;
348
+ found = {
349
+ binding: path.scope.getBinding(targetName) ?? null,
350
+ arrayNode: path.node.init
351
+ };
352
+ },
353
+ AssignmentExpression(path) {
354
+ if (found) return;
355
+ if (!t.isIdentifier(path.node.left, { name: targetName })) return;
356
+ if (!t.isArrayExpression(path.node.right)) return;
357
+ if (!isPrimitiveArray(path.node.right)) return;
358
+ found = {
359
+ binding: path.scope.getBinding(targetName) ?? null,
360
+ arrayNode: path.node.right
361
+ };
362
+ }
363
+ });
364
+ if (!found) return null;
365
+ let hasRiskOfSideEffects = false;
366
+ patchDefault(traverse2)(ast, {
367
+ MemberExpression(path) {
368
+ if (hasRiskOfSideEffects) return;
369
+ if (!t.isIdentifier(path.node.object, { name: targetName })) return;
370
+ const parent = path.parentPath;
371
+ if (!parent) return;
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
+ }
388
+ }
389
+ });
390
+ const result = found;
391
+ result.hasRiskOfSideEffects = hasRiskOfSideEffects;
392
+ return result;
393
+ };
394
+ var expandArrayAccess = async (code, filename, targetName) => {
395
+ const ast = parse3(code, createParseOptions(filename));
396
+ const targetArray = findTargetArray(ast, targetName);
397
+ if (!targetArray) {
398
+ throw new Error(`Target array '${targetName}' is not a primitive array`);
399
+ }
400
+ if (targetArray.hasRiskOfSideEffects) {
401
+ const continueAnswer = await createPrompt(
402
+ "The target array has risk of side effects, do you want to continue? (y/n)"
403
+ );
404
+ if (continueAnswer !== "y") {
405
+ throw new Error("User cancelled");
406
+ }
407
+ }
408
+ const candidates = [];
409
+ const mutatedInfo = collectMutatedIndexes(ast, targetName);
410
+ if (mutatedInfo.hasUnknownMutations) {
411
+ return {
412
+ code: patchDefault(generate2)(ast).code,
413
+ replacedCount: 0
414
+ };
415
+ }
416
+ patchDefault(traverse2)(ast, {
417
+ MemberExpression(path) {
418
+ if (!path.node.computed) return;
419
+ if (isAssignmentTarget(path)) return;
420
+ if (!t.isIdentifier(path.node.object, { name: targetName })) return;
421
+ if (targetArray.binding && path.scope.getBinding(targetName) !== targetArray.binding) {
422
+ return;
423
+ }
424
+ if (!t.isExpression(path.node.property)) return;
425
+ const index = getIndexFromProperty(path.node.property);
426
+ if (index === null || index < 0) return;
427
+ if (mutatedInfo.mutatedIndexes.has(index)) return;
428
+ const element = targetArray.arrayNode.elements[index];
429
+ if (!element || t.isSpreadElement(element)) return;
430
+ candidates.push({
431
+ path,
432
+ replacement: t.cloneNode(element, true)
433
+ });
434
+ }
435
+ });
436
+ let replacedCount = 0;
437
+ for (const candidate of candidates) {
438
+ const original = t.cloneNode(candidate.path.node, true);
439
+ candidate.path.replaceWith(t.cloneNode(candidate.replacement, true));
440
+ const nextCode = patchDefault(generate2)(ast).code;
441
+ try {
442
+ parse3(nextCode, createParseOptions(filename));
443
+ replacedCount += 1;
444
+ } catch {
445
+ candidate.path.replaceWith(original);
446
+ }
447
+ }
448
+ return {
449
+ code: patchDefault(generate2)(ast).code,
450
+ replacedCount
451
+ };
452
+ };
453
+ var expand_array_default = createCommand((program2) => {
454
+ program2.command("expand-array").description("Expand array index access for primitive values").argument("[file]", "The file to transform").option("--input, --file <file>", "The file to transform").option("--target <name>", "Target array variable name").option("--o, --output <file>", "Output file path").option("--unlimited", "Unlimited timeout").action(
455
+ async (fileArgument, options) => {
456
+ await timeout(
457
+ async ({ finish }) => {
458
+ const filename = fileArgument ?? options.file ?? await createPrompt("Enter the file path:");
459
+ if (!filename) {
460
+ showError("No file provided");
461
+ return finish();
462
+ }
463
+ const targetName = options.target ?? await createPrompt("Enter the target variable name:");
464
+ if (!targetName) {
465
+ showError("No target variable provided");
466
+ return finish();
467
+ }
468
+ try {
469
+ const fileContent = readFileSync3(filename, "utf8");
470
+ const defaultOutputPath = createDefaultOutputPath2(filename);
471
+ let outputPath = options.output;
472
+ if (!outputPath) {
473
+ const promptPath = (await createPrompt("Enter the output file path:"))?.trim();
474
+ outputPath = promptPath || defaultOutputPath;
475
+ }
476
+ const loader = loading3("Expanding array access...").start();
477
+ try {
478
+ const { code: output, replacedCount } = await expandArrayAccess(
479
+ fileContent,
480
+ filename,
481
+ targetName
482
+ );
483
+ writeFileSync2(outputPath, output, "utf8");
484
+ loader.succeed(
485
+ `Saved expand-array file to: ${outputPath} (${diff(fileContent, output).length} lines changed, ${replacedCount} replacements)`
486
+ );
487
+ return finish();
488
+ } catch (error) {
489
+ loader.fail("Failed to apply expand-array transform");
490
+ showError(
491
+ `Error transforming file '${filename}': ${error instanceof Error ? error.message : "Unknown error"}`
492
+ );
493
+ return finish();
494
+ }
495
+ } catch (error) {
496
+ showError(
497
+ `Error reading file '${filename}': ${error instanceof Error ? error.message : "Unknown error"}`
498
+ );
499
+ return finish();
500
+ }
501
+ },
502
+ options.unlimited ? null : 120 * 1e3
503
+ );
504
+ }
505
+ );
506
+ });
507
+
508
+ // commands/expand-object/index.ts
509
+ import { readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
510
+ import { basename as basename3, dirname as dirname3, extname as extname3, join as join3 } from "path";
511
+ import { parse as parse4 } from "@babel/parser";
512
+ import traverse3 from "@babel/traverse";
513
+ import generate3 from "@babel/generator";
514
+ import * as t2 from "@babel/types";
515
+ import loading4 from "loading-cli";
516
+ var createDefaultOutputPath3 = (inputPath) => {
517
+ const ext = extname3(inputPath);
518
+ if (!ext) {
519
+ return `${inputPath}.expand-object.js`;
520
+ }
521
+ const base = basename3(inputPath, 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}`);
794
+ };
795
+ var isSupportedNumberOperator = (operator) => {
796
+ return operator === "-" || operator === "*" || operator === "/" || operator === "%" || operator === "**" || operator === "<<" || operator === ">>" || operator === ">>>" || operator === "|" || operator === "&" || operator === "^";
797
+ };
798
+ var resolveBindingValue = (binding, state) => {
799
+ if (!binding.constant || binding.kind !== "const") {
800
+ return null;
801
+ }
802
+ const cached = state.bindingValues.get(binding);
803
+ if (cached !== void 0) {
804
+ return cached;
805
+ }
806
+ if (state.bindingStack.has(binding)) {
807
+ return null;
808
+ }
809
+ state.bindingStack.add(binding);
810
+ let value = null;
811
+ if (binding.path.isVariableDeclarator()) {
812
+ if (binding.path.node.init) {
813
+ const initPath = binding.path.get("init");
814
+ if (!Array.isArray(initPath) && initPath.isExpression()) {
815
+ value = evaluateExpression(initPath, state);
816
+ }
817
+ }
818
+ }
819
+ state.bindingStack.delete(binding);
820
+ if (value !== null) {
821
+ state.bindingValues.set(binding, value);
822
+ }
823
+ return value;
824
+ };
825
+ var evaluateExpression = (path, state) => {
826
+ if (path.isNumericLiteral()) {
827
+ return path.node.value;
828
+ }
829
+ if (path.isStringLiteral()) {
830
+ return path.node.value;
831
+ }
832
+ if (path.isParenthesizedExpression()) {
833
+ const inner = path.get("expression");
834
+ if (!Array.isArray(inner) && inner.isExpression()) {
835
+ return evaluateExpression(inner, state);
836
+ }
837
+ return null;
838
+ }
839
+ if (path.isIdentifier()) {
840
+ const binding = path.scope.getBinding(path.node.name);
841
+ if (!binding) return null;
842
+ return resolveBindingValue(binding, state);
843
+ }
844
+ if (path.isUnaryExpression()) {
845
+ const argumentPath = path.get("argument");
846
+ if (Array.isArray(argumentPath) || !argumentPath.isExpression()) {
847
+ return null;
848
+ }
849
+ const argument = evaluateExpression(argumentPath, state);
850
+ if (argument === null || typeof argument !== "number") {
851
+ return null;
852
+ }
853
+ if (path.node.operator === "+") return +argument;
854
+ if (path.node.operator === "-") return -argument;
855
+ if (path.node.operator === "~") return ~argument;
856
+ return null;
857
+ }
858
+ if (path.isBinaryExpression()) {
859
+ const leftPath = path.get("left");
860
+ const rightPath = path.get("right");
861
+ if (Array.isArray(leftPath) || Array.isArray(rightPath) || !leftPath.isExpression() || !rightPath.isExpression()) {
862
+ return null;
863
+ }
864
+ const left = evaluateExpression(leftPath, state);
865
+ const right = evaluateExpression(rightPath, state);
866
+ if (left === null || right === null) {
867
+ return null;
868
+ }
869
+ if (path.node.operator === "+") {
870
+ if (typeof left === "string" || typeof right === "string") {
871
+ return `${left}${right}`;
872
+ }
873
+ if (typeof left === "number" && typeof right === "number") {
874
+ return left + right;
875
+ }
876
+ return null;
877
+ }
878
+ if (typeof left !== "number" || typeof right !== "number" || !isSupportedNumberOperator(path.node.operator)) {
879
+ return null;
880
+ }
881
+ switch (path.node.operator) {
882
+ case "-":
883
+ return left - right;
884
+ case "*":
885
+ return left * right;
886
+ case "/":
887
+ return left / right;
888
+ case "%":
889
+ return left % right;
890
+ case "**":
891
+ return left ** right;
892
+ case "<<":
893
+ return left << right;
894
+ case ">>":
895
+ return left >> right;
896
+ case ">>>":
897
+ return left >>> right;
898
+ case "|":
899
+ return left | right;
900
+ case "&":
901
+ return left & right;
902
+ case "^":
903
+ return left ^ right;
904
+ default:
905
+ return null;
906
+ }
907
+ }
908
+ return null;
909
+ };
910
+ var shouldSkipReferencedIdentifier = (path) => {
911
+ const parent = path.parentPath;
912
+ if (!parent) return false;
913
+ if (parent.isObjectProperty()) {
914
+ if (parent.node.shorthand) return true;
915
+ if (parent.get("key") === path && !parent.node.computed) {
916
+ return true;
917
+ }
918
+ }
919
+ if (parent.isObjectMethod()) {
920
+ if (parent.get("key") === path && !parent.node.computed) {
921
+ return true;
922
+ }
923
+ }
924
+ return false;
925
+ };
926
+ var preEvaluate = (code, filename) => {
927
+ const ast = parse5(code, createParseOptions(filename));
928
+ const state = {
929
+ bindingValues: /* @__PURE__ */ new Map(),
930
+ bindingStack: /* @__PURE__ */ new Set()
931
+ };
932
+ let replacedCount = 0;
933
+ patchDefault(traverse4)(ast, {
934
+ ReferencedIdentifier(path) {
935
+ if (shouldSkipReferencedIdentifier(path)) {
936
+ return;
937
+ }
938
+ const value = evaluateExpression(path, state);
939
+ if (value === null) return;
940
+ if (typeof value === "number") {
941
+ path.replaceWith(t3.numericLiteral(value));
942
+ } else {
943
+ path.replaceWith(t3.stringLiteral(value));
944
+ }
945
+ replacedCount += 1;
946
+ },
947
+ UnaryExpression: {
948
+ exit(path) {
949
+ const value = evaluateExpression(path, state);
950
+ if (value === null) return;
951
+ if (typeof value === "number") {
952
+ path.replaceWith(t3.numericLiteral(value));
953
+ } else {
954
+ path.replaceWith(t3.stringLiteral(value));
955
+ }
956
+ replacedCount += 1;
957
+ }
958
+ },
959
+ BinaryExpression: {
960
+ exit(path) {
961
+ const value = evaluateExpression(path, state);
962
+ if (value === null) return;
963
+ if (typeof value === "number") {
964
+ path.replaceWith(t3.numericLiteral(value));
965
+ } else {
966
+ path.replaceWith(t3.stringLiteral(value));
967
+ }
968
+ replacedCount += 1;
969
+ }
970
+ },
971
+ ParenthesizedExpression: {
972
+ exit(path) {
973
+ const value = evaluateExpression(path, state);
974
+ if (value === null) return;
975
+ if (typeof value === "number") {
976
+ path.replaceWith(t3.numericLiteral(value));
977
+ } else {
978
+ path.replaceWith(t3.stringLiteral(value));
979
+ }
980
+ replacedCount += 1;
981
+ }
982
+ }
983
+ });
984
+ return {
985
+ code: patchDefault(generate4)(ast).code,
986
+ replacedCount
987
+ };
988
+ };
989
+ var pre_evaluate_default = createCommand((program2) => {
990
+ program2.command("pre-evaluate").description("Pre-evaluate const numeric/string expressions").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(
991
+ async (fileArgument, options) => {
992
+ await timeout(
993
+ async ({ finish }) => {
994
+ const filename = fileArgument ?? options.file ?? await createPrompt("Enter the file path:");
995
+ if (!filename) {
996
+ showError("No file provided");
997
+ return finish();
998
+ }
999
+ try {
1000
+ const fileContent = readFileSync5(filename, "utf8");
1001
+ const defaultOutputPath = createDefaultOutputPath4(filename);
1002
+ let outputPath = options.output;
1003
+ if (!outputPath) {
1004
+ const promptPath = (await createPrompt("Enter the output file path:"))?.trim();
1005
+ outputPath = promptPath || defaultOutputPath;
1006
+ }
1007
+ const loader = loading5("Pre-evaluating constants...").start();
1008
+ try {
1009
+ const { code: output, replacedCount } = preEvaluate(
1010
+ fileContent,
1011
+ filename
1012
+ );
1013
+ writeFileSync4(outputPath, output, "utf8");
1014
+ loader.succeed(
1015
+ `Saved pre-evaluate file to: ${outputPath} (${diff(fileContent, output).length} lines changed, ${replacedCount} replacements)`
1016
+ );
1017
+ return finish();
1018
+ } catch (error) {
1019
+ loader.fail("Failed to apply pre-evaluate transform");
1020
+ showError(
1021
+ `Error transforming file '${filename}': ${error instanceof Error ? error.message : "Unknown error"}`
1022
+ );
1023
+ return finish();
1024
+ }
1025
+ } catch (error) {
1026
+ showError(
1027
+ `Error reading file '${filename}': ${error instanceof Error ? error.message : "Unknown error"}`
1028
+ );
1029
+ return finish();
1030
+ }
1031
+ },
1032
+ options.unlimited ? null : 120 * 1e3
1033
+ );
1034
+ }
1035
+ );
1036
+ });
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
+
264
1144
  // utils/cli/showCredit.ts
265
1145
  import chalk3 from "chalk";
266
1146
  var beautify = (strings, ...values) => {
@@ -285,10 +1165,10 @@ var calmGradienrain = (text) => {
285
1165
  const endHue = 300;
286
1166
  const saturation = 0.45;
287
1167
  const value = 0.8;
288
- const ease = (t) => t * t * (3 - 2 * t);
1168
+ const ease = (t5) => t5 * t5 * (3 - 2 * t5);
289
1169
  return text.split("").map((char, i) => {
290
- const t = ease(i / Math.max(text.length - 1, 1));
291
- const hue = startHue + (endHue - startHue) * t;
1170
+ const t5 = ease(i / Math.max(text.length - 1, 1));
1171
+ const hue = startHue + (endHue - startHue) * t5;
292
1172
  const c = value * saturation;
293
1173
  const h = hue / 60;
294
1174
  const x = c * (1 - Math.abs(h % 2 - 1));
@@ -312,10 +1192,10 @@ ${calmGradienrain(`Expose Kit v${VERSION}`)}
312
1192
  `;
313
1193
 
314
1194
  // index.ts
315
- import { readFileSync as readFileSync3 } from "fs";
1195
+ import { readFileSync as readFileSync7 } from "fs";
316
1196
  var __filename = fileURLToPath(import.meta.url);
317
- var __dirname = dirname2(__filename);
318
- var pkg = JSON.parse(readFileSync3(join2(__dirname, "package.json"), "utf8"));
1197
+ var __dirname = dirname6(__filename);
1198
+ var pkg = JSON.parse(readFileSync7(join6(__dirname, "package.json"), "utf8"));
319
1199
  console.log(showCredit(pkg.version));
320
1200
  console.log();
321
1201
  var program = new Command();
@@ -324,7 +1204,14 @@ program.name("expose").description("CLI for Deobfuscating").version(
324
1204
  "-v, --version",
325
1205
  "display version number"
326
1206
  );
327
- var commands = [parsable_default, safe_scope_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
+ ];
328
1215
  for (const command of commands) {
329
1216
  command(program);
330
1217
  }
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expose-kit",
3
- "version": "0.3.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.3.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)",