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 +101 -2
- package/dist/index.js +895 -8
- package/dist/package.json +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -105,7 +105,7 @@ With this alone:
|
|
|
105
105
|
### 3. Apply transforms step by step
|
|
106
106
|
|
|
107
107
|
After `safe-scope`, combine common techniques like:
|
|
108
|
-
- `expand-array` and more
|
|
108
|
+
- `expand-array`, `expand-object` and more
|
|
109
109
|
- legacy obfuscator-specific commands
|
|
110
110
|
|
|
111
111
|
After **each step**, run `parsable` again.
|
|
@@ -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:
|
|
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
|
|
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 = (
|
|
1168
|
+
const ease = (t5) => t5 * t5 * (3 - 2 * t5);
|
|
289
1169
|
return text.split("").map((char, i) => {
|
|
290
|
-
const
|
|
291
|
-
const hue = startHue + (endHue - startHue) *
|
|
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
|
|
1195
|
+
import { readFileSync as readFileSync7 } from "fs";
|
|
316
1196
|
var __filename = fileURLToPath(import.meta.url);
|
|
317
|
-
var __dirname =
|
|
318
|
-
var pkg = JSON.parse(
|
|
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 = [
|
|
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