expose-kit 0.7.0 → 0.8.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
@@ -285,6 +285,42 @@ Notes:
285
285
 
286
286
  ---
287
287
 
288
+ ### `expose remove-reassign`
289
+
290
+ Inline safe alias assignments and wrapper calls.
291
+ ```js
292
+ const a = 0;
293
+ const b = a;
294
+ const c = b;
295
+ console.log(c); // => console.log(a);
296
+ ```
297
+ ```js
298
+ function a(arg) {
299
+ return b(arg);
300
+ }
301
+ function c(arg) {
302
+ return d[arg];
303
+ }
304
+ a(0); // => b(0)
305
+ c(0); // => d[0]
306
+ ```
307
+ Example is [here](https://github.com/evex-dev/expose-kit/tree/main/commands/remove-reassign/mocks).
308
+
309
+ ```bash
310
+ expose remove-reassign path/to/file.js --output path/to/file.remove-reassign.js
311
+ ```
312
+
313
+ Args:
314
+ - `--o, --output <file>`
315
+ Output file path
316
+
317
+ Notes:
318
+ - Only inlines const/immutable alias chains.
319
+ - Skips object shorthand replacements and shadowed bindings.
320
+ - Wrapper inlining is limited to single-return passthroughs.
321
+
322
+ ---
323
+
288
324
  ### `expose remove-unused`
289
325
 
290
326
  Remove unused variabless.
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 dirname8, join as join8 } from "path";
5
+ import { dirname as dirname9, join as join9 } from "path";
6
6
  import { fileURLToPath } from "url";
7
7
  import chalk4 from "chalk";
8
8
 
@@ -1311,7 +1311,7 @@ var remove_updater_default = createCommand((program2) => {
1311
1311
  );
1312
1312
  });
1313
1313
 
1314
- // commands/remove-unused/index.ts
1314
+ // commands/remove-reassign/index.ts
1315
1315
  import { readFileSync as readFileSync8, writeFileSync as writeFileSync7 } from "fs";
1316
1316
  import { basename as basename7, dirname as dirname7, extname as extname7, join as join7 } from "path";
1317
1317
  import { parse as parse8 } from "@babel/parser";
@@ -1322,19 +1322,259 @@ import loading8 from "loading-cli";
1322
1322
  var createDefaultOutputPath7 = (inputPath) => {
1323
1323
  const ext = extname7(inputPath);
1324
1324
  if (!ext) {
1325
- return `${inputPath}.remove-unused.js`;
1325
+ return `${inputPath}.remove-reassign.js`;
1326
1326
  }
1327
1327
  const base = basename7(inputPath, ext);
1328
- return join7(dirname7(inputPath), `${base}.remove-unused${ext}`);
1328
+ return join7(dirname7(inputPath), `${base}.remove-reassign${ext}`);
1329
1329
  };
1330
- var removeUnusedVariables = (code, filename) => {
1330
+ var isShorthandObjectKey = (path) => {
1331
+ const parent = path.parentPath;
1332
+ if (!parent || !parent.isObjectProperty()) return false;
1333
+ return parent.node.shorthand && parent.get("key") === path;
1334
+ };
1335
+ var resolveFinalAlias = (binding, getBinding) => {
1336
+ const visited = /* @__PURE__ */ new Set();
1337
+ let currentBinding = binding;
1338
+ let currentName = null;
1339
+ while (currentBinding && !visited.has(currentBinding)) {
1340
+ visited.add(currentBinding);
1341
+ if (!t6.isVariableDeclarator(currentBinding.path.node)) break;
1342
+ const init = currentBinding.path.node.init;
1343
+ if (!init || !t6.isIdentifier(init)) break;
1344
+ const nextBinding = getBinding(init.name);
1345
+ if (!nextBinding || !nextBinding.constant) break;
1346
+ currentBinding = nextBinding;
1347
+ currentName = init.name;
1348
+ }
1349
+ if (!currentBinding || !currentName) {
1350
+ return null;
1351
+ }
1352
+ return { targetBinding: currentBinding, targetName: currentName };
1353
+ };
1354
+ var getReturnExpression = (functionPath) => {
1355
+ const body = functionPath.node.body;
1356
+ if (!t6.isBlockStatement(body)) {
1357
+ return body;
1358
+ }
1359
+ if (body.body.length !== 1) return null;
1360
+ const statement = body.body[0];
1361
+ if (!t6.isReturnStatement(statement) || !statement.argument) return null;
1362
+ return statement.argument;
1363
+ };
1364
+ var getWrapperInfo = (functionPath, wrapperBinding) => {
1365
+ const params = functionPath.node.params;
1366
+ if (params.length === 0) return null;
1367
+ const paramNames = [];
1368
+ for (const param of params) {
1369
+ if (!t6.isIdentifier(param)) return null;
1370
+ paramNames.push(param.name);
1371
+ }
1372
+ const expression = getReturnExpression(functionPath);
1373
+ if (!expression) return null;
1374
+ if (t6.isCallExpression(expression)) {
1375
+ if (!t6.isIdentifier(expression.callee)) return null;
1376
+ if (expression.arguments.length !== paramNames.length) return null;
1377
+ for (let i = 0; i < expression.arguments.length; i++) {
1378
+ const arg = expression.arguments[i];
1379
+ if (!t6.isIdentifier(arg)) return null;
1380
+ if (arg.name !== paramNames[i]) return null;
1381
+ }
1382
+ const targetBinding = functionPath.scope.getBinding(expression.callee.name);
1383
+ if (!targetBinding) return null;
1384
+ return {
1385
+ kind: "call",
1386
+ wrapperBinding,
1387
+ targetName: expression.callee.name,
1388
+ targetBinding,
1389
+ paramNames
1390
+ };
1391
+ }
1392
+ if (t6.isMemberExpression(expression) && expression.computed && t6.isIdentifier(expression.object) && t6.isIdentifier(expression.property) && paramNames.length === 1 && expression.property.name === paramNames[0]) {
1393
+ const targetBinding = functionPath.scope.getBinding(expression.object.name);
1394
+ if (!targetBinding) return null;
1395
+ return {
1396
+ kind: "member",
1397
+ wrapperBinding,
1398
+ targetName: expression.object.name,
1399
+ targetBinding
1400
+ };
1401
+ }
1402
+ return null;
1403
+ };
1404
+ var removeReassign = (code, filename) => {
1331
1405
  const ast = parse8(code, createParseOptions(filename));
1332
- let changed = false;
1406
+ const aliases = [];
1407
+ const wrappers = [];
1408
+ patchDefault(traverse7)(ast, {
1409
+ VariableDeclarator(path) {
1410
+ if (!t6.isIdentifier(path.node.id)) return;
1411
+ if (!path.node.init || !t6.isIdentifier(path.node.init)) return;
1412
+ const binding = path.scope.getBinding(path.node.id.name);
1413
+ if (!binding || !binding.constant) return;
1414
+ const targetBinding = path.scope.getBinding(path.node.init.name);
1415
+ if (!targetBinding || !targetBinding.constant) return;
1416
+ const resolved = resolveFinalAlias(
1417
+ binding,
1418
+ (name) => path.scope.getBinding(name)
1419
+ );
1420
+ if (!resolved) return;
1421
+ aliases.push({
1422
+ aliasBinding: binding,
1423
+ targetBinding: resolved.targetBinding,
1424
+ targetName: resolved.targetName
1425
+ });
1426
+ },
1427
+ FunctionDeclaration(path) {
1428
+ if (!path.node.id) return;
1429
+ const binding = path.scope.getBinding(path.node.id.name);
1430
+ if (!binding) return;
1431
+ const wrapper = getWrapperInfo(path, binding);
1432
+ if (wrapper) wrappers.push(wrapper);
1433
+ }
1434
+ });
1333
1435
  patchDefault(traverse7)(ast, {
1436
+ VariableDeclarator(path) {
1437
+ if (!t6.isIdentifier(path.node.id)) return;
1438
+ const initPath = path.get("init");
1439
+ if (!initPath || !initPath.isFunctionExpression() && !initPath.isArrowFunctionExpression()) {
1440
+ return;
1441
+ }
1442
+ const binding = path.scope.getBinding(path.node.id.name);
1443
+ if (!binding || !binding.constant) return;
1444
+ const wrapper = getWrapperInfo(
1445
+ initPath,
1446
+ binding
1447
+ );
1448
+ if (wrapper) wrappers.push(wrapper);
1449
+ }
1450
+ });
1451
+ let aliasReplacedCount = 0;
1452
+ let wrapperReplacedCount = 0;
1453
+ for (const alias of aliases) {
1454
+ for (const referencePath of alias.aliasBinding.referencePaths) {
1455
+ if (isShorthandObjectKey(referencePath)) continue;
1456
+ const targetBinding = referencePath.scope.getBinding(alias.targetName);
1457
+ if (targetBinding !== alias.targetBinding) continue;
1458
+ referencePath.replaceWith(t6.identifier(alias.targetName));
1459
+ aliasReplacedCount += 1;
1460
+ }
1461
+ }
1462
+ const wrapperMap = /* @__PURE__ */ new Map();
1463
+ for (const wrapper of wrappers) {
1464
+ wrapperMap.set(wrapper.wrapperBinding, wrapper);
1465
+ }
1466
+ patchDefault(traverse7)(ast, {
1467
+ CallExpression(path) {
1468
+ if (!t6.isIdentifier(path.node.callee)) return;
1469
+ const calleeBinding = path.scope.getBinding(path.node.callee.name);
1470
+ if (!calleeBinding) return;
1471
+ const wrapper = wrapperMap.get(calleeBinding);
1472
+ if (!wrapper) return;
1473
+ const targetBinding = path.scope.getBinding(wrapper.targetName);
1474
+ if (targetBinding !== wrapper.targetBinding) return;
1475
+ if (wrapper.kind === "call") {
1476
+ if (path.node.arguments.length !== wrapper.paramNames.length) return;
1477
+ for (const arg2 of path.node.arguments) {
1478
+ if (t6.isSpreadElement(arg2)) return;
1479
+ }
1480
+ const nextArgs = path.node.arguments.map(
1481
+ (arg2) => t6.cloneNode(arg2, true)
1482
+ );
1483
+ path.replaceWith(
1484
+ t6.callExpression(t6.identifier(wrapper.targetName), nextArgs)
1485
+ );
1486
+ wrapperReplacedCount += 1;
1487
+ return;
1488
+ }
1489
+ if (path.node.arguments.length !== 1) return;
1490
+ const arg = path.node.arguments[0];
1491
+ if (!t6.isExpression(arg) || t6.isSpreadElement(arg)) return;
1492
+ path.replaceWith(
1493
+ t6.memberExpression(
1494
+ t6.identifier(wrapper.targetName),
1495
+ t6.cloneNode(arg, true),
1496
+ true
1497
+ )
1498
+ );
1499
+ wrapperReplacedCount += 1;
1500
+ }
1501
+ });
1502
+ return {
1503
+ code: patchDefault(generate7)(ast).code,
1504
+ aliasReplacedCount,
1505
+ wrapperReplacedCount
1506
+ };
1507
+ };
1508
+ var remove_reassign_default = createCommand((program2) => {
1509
+ program2.command("remove-reassign").description("Inline safe alias assignments and wrapper calls").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(
1510
+ async (fileArgument, options) => {
1511
+ await timeout(
1512
+ async ({ finish }) => {
1513
+ const filename = fileArgument ?? options.file ?? await createPrompt("Enter the file path:");
1514
+ if (!filename) {
1515
+ showError("No file provided");
1516
+ return finish();
1517
+ }
1518
+ try {
1519
+ const fileContent = readFileSync8(filename, "utf8");
1520
+ const defaultOutputPath = createDefaultOutputPath7(filename);
1521
+ let outputPath = options.output;
1522
+ if (!outputPath) {
1523
+ const promptPath = (await createPrompt("Enter the output file path:"))?.trim();
1524
+ outputPath = promptPath || defaultOutputPath;
1525
+ }
1526
+ const loader = loading8("Removing reassign aliases...").start();
1527
+ try {
1528
+ const { code: output, aliasReplacedCount, wrapperReplacedCount } = removeReassign(fileContent, filename);
1529
+ writeFileSync7(outputPath, output, "utf8");
1530
+ loader.succeed(
1531
+ `Saved remove-reassign file to: ${outputPath} (${diff(fileContent, output).length} lines changed, ${aliasReplacedCount} aliases, ${wrapperReplacedCount} calls inlined)`
1532
+ );
1533
+ return finish();
1534
+ } catch (error) {
1535
+ loader.fail("Failed to apply remove-reassign transform");
1536
+ showError(
1537
+ `Error transforming file '${filename}': ${error instanceof Error ? error.message : "Unknown error"}`
1538
+ );
1539
+ return finish();
1540
+ }
1541
+ } catch (error) {
1542
+ showError(
1543
+ `Error reading file '${filename}': ${error instanceof Error ? error.message : "Unknown error"}`
1544
+ );
1545
+ return finish();
1546
+ }
1547
+ },
1548
+ options.unlimited ? null : 120 * 1e3
1549
+ );
1550
+ }
1551
+ );
1552
+ });
1553
+
1554
+ // commands/remove-unused/index.ts
1555
+ import { readFileSync as readFileSync9, writeFileSync as writeFileSync8 } from "fs";
1556
+ import { basename as basename8, dirname as dirname8, extname as extname8, join as join8 } from "path";
1557
+ import { parse as parse9 } from "@babel/parser";
1558
+ import traverse8 from "@babel/traverse";
1559
+ import generate8 from "@babel/generator";
1560
+ import * as t7 from "@babel/types";
1561
+ import loading9 from "loading-cli";
1562
+ var createDefaultOutputPath8 = (inputPath) => {
1563
+ const ext = extname8(inputPath);
1564
+ if (!ext) {
1565
+ return `${inputPath}.remove-unused.js`;
1566
+ }
1567
+ const base = basename8(inputPath, ext);
1568
+ return join8(dirname8(inputPath), `${base}.remove-unused${ext}`);
1569
+ };
1570
+ var removeUnusedVariables = (code, filename) => {
1571
+ const ast = parse9(code, createParseOptions(filename));
1572
+ let changed = false;
1573
+ patchDefault(traverse8)(ast, {
1334
1574
  Scope(path) {
1335
1575
  for (const binding of Object.values(path.scope.bindings)) {
1336
1576
  if (!binding.referenced && binding.constantViolations.length === 0 && binding.path.key !== "handler" && !binding.path.isFunctionExpression()) {
1337
- if (t6.isProgram(binding.scope.block) && (binding.kind === "var" || binding.kind === "hoisted")) {
1577
+ if (t7.isProgram(binding.scope.block) && (binding.kind === "var" || binding.kind === "hoisted")) {
1338
1578
  continue;
1339
1579
  }
1340
1580
  const targets = binding.path.parentKey === "params" ? [...binding.referencePaths, ...binding.constantViolations] : [
@@ -1343,11 +1583,11 @@ var removeUnusedVariables = (code, filename) => {
1343
1583
  ...binding.constantViolations
1344
1584
  ];
1345
1585
  for (const targetPath of targets) {
1346
- if (targetPath.isVariableDeclarator() && (t6.isArrayPattern(targetPath.node.id) && targetPath.node.id.elements.length > 1 || t6.isObjectPattern(targetPath.node.id) && targetPath.node.id.properties.length > 1)) {
1586
+ if (targetPath.isVariableDeclarator() && (t7.isArrayPattern(targetPath.node.id) && targetPath.node.id.elements.length > 1 || t7.isObjectPattern(targetPath.node.id) && targetPath.node.id.properties.length > 1)) {
1347
1587
  continue;
1348
1588
  }
1349
1589
  if (targetPath.key === "consequent" || targetPath.key === "alternate" || targetPath.key === "body") {
1350
- targetPath.replaceWith(t6.blockStatement([]));
1590
+ targetPath.replaceWith(t7.blockStatement([]));
1351
1591
  } else {
1352
1592
  const parentPath = targetPath.parentPath;
1353
1593
  if (parentPath?.isVariableDeclaration() && parentPath.node.declarations.length === 1) {
@@ -1363,7 +1603,7 @@ var removeUnusedVariables = (code, filename) => {
1363
1603
  }
1364
1604
  });
1365
1605
  return {
1366
- code: patchDefault(generate7)(ast).code,
1606
+ code: patchDefault(generate8)(ast).code,
1367
1607
  changed
1368
1608
  };
1369
1609
  };
@@ -1378,20 +1618,20 @@ var remove_unused_default = createCommand((program2) => {
1378
1618
  return finish();
1379
1619
  }
1380
1620
  try {
1381
- const fileContent = readFileSync8(filename, "utf8");
1382
- const defaultOutputPath = createDefaultOutputPath7(filename);
1621
+ const fileContent = readFileSync9(filename, "utf8");
1622
+ const defaultOutputPath = createDefaultOutputPath8(filename);
1383
1623
  let outputPath = options.output;
1384
1624
  if (!outputPath) {
1385
1625
  const promptPath = (await createPrompt("Enter the output file path:"))?.trim();
1386
1626
  outputPath = promptPath || defaultOutputPath;
1387
1627
  }
1388
- const loader = loading8("Removing unused variables...").start();
1628
+ const loader = loading9("Removing unused variables...").start();
1389
1629
  try {
1390
1630
  const { code: output, changed } = removeUnusedVariables(
1391
1631
  fileContent,
1392
1632
  filename
1393
1633
  );
1394
- writeFileSync7(outputPath, output, "utf8");
1634
+ writeFileSync8(outputPath, output, "utf8");
1395
1635
  const diffLines = diff(fileContent, output).length;
1396
1636
  loader.succeed(
1397
1637
  `Saved remove-unused file to: ${outputPath} (${diffLines} lines changed${changed ? ", removed unused declarations" : ", no changes"})`
@@ -1441,10 +1681,10 @@ var calmGradienrain = (text) => {
1441
1681
  const endHue = 300;
1442
1682
  const saturation = 0.45;
1443
1683
  const value = 0.8;
1444
- const ease = (t7) => t7 * t7 * (3 - 2 * t7);
1684
+ const ease = (t8) => t8 * t8 * (3 - 2 * t8);
1445
1685
  return text.split("").map((char, i) => {
1446
- const t7 = ease(i / Math.max(text.length - 1, 1));
1447
- const hue = startHue + (endHue - startHue) * t7;
1686
+ const t8 = ease(i / Math.max(text.length - 1, 1));
1687
+ const hue = startHue + (endHue - startHue) * t8;
1448
1688
  const c = value * saturation;
1449
1689
  const h = hue / 60;
1450
1690
  const x = c * (1 - Math.abs(h % 2 - 1));
@@ -1468,10 +1708,10 @@ ${calmGradienrain(`Expose Kit v${VERSION}`)}
1468
1708
  `;
1469
1709
 
1470
1710
  // index.ts
1471
- import { readFileSync as readFileSync9 } from "fs";
1711
+ import { readFileSync as readFileSync10 } from "fs";
1472
1712
  var __filename = fileURLToPath(import.meta.url);
1473
- var __dirname = dirname8(__filename);
1474
- var pkg = JSON.parse(readFileSync9(join8(__dirname, "package.json"), "utf8"));
1713
+ var __dirname = dirname9(__filename);
1714
+ var pkg = JSON.parse(readFileSync10(join9(__dirname, "package.json"), "utf8"));
1475
1715
  console.log(showCredit(pkg.version));
1476
1716
  console.log();
1477
1717
  var program = new Command();
@@ -1488,6 +1728,7 @@ var commands = [
1488
1728
  object_packer_default,
1489
1729
  pre_evaluate_default,
1490
1730
  remove_updater_default,
1731
+ remove_reassign_default,
1491
1732
  remove_unused_default
1492
1733
  ];
1493
1734
  for (const command of commands) {
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expose-kit",
3
- "version": "0.7.0",
3
+ "version": "0.8.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.7.0",
3
+ "version": "0.8.0",
4
4
  "type": "module",
5
5
  "private": false,
6
6
  "author": "EdamAmex <edame8080@gmail.com> (https://github.com/EdamAme-x)",