superjs-core 0.1.0 → 0.3.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/dist/index.js CHANGED
@@ -1365,15 +1365,451 @@ function getType2(value) {
1365
1365
  if (value instanceof Promise) return "promise";
1366
1366
  return "object";
1367
1367
  }
1368
+
1369
+ // src/dep-exray/scanner/index.ts
1370
+ import { readFileSync, readdirSync, existsSync } from "fs";
1371
+ import { join as join2, basename as basename2 } from "path";
1372
+
1373
+ // src/dep-exray/known-mappings.ts
1374
+ var KNOWN_MAPPINGS = [
1375
+ {
1376
+ name: "lodash",
1377
+ size: "4.2 MB",
1378
+ replacement: "jscore-core",
1379
+ confidence: "high",
1380
+ autoPrReady: true,
1381
+ reason: "Most lodash functions have direct replacements in jscore-core with 99% API compatibility",
1382
+ detectionPattern: `from ['"]lodash['"]|require\\(['"]lodash['"]\\)`
1383
+ },
1384
+ {
1385
+ name: "moment",
1386
+ size: "2.5 MB",
1387
+ replacement: "jscore-core/date",
1388
+ confidence: "high",
1389
+ autoPrReady: true,
1390
+ reason: "date utilities in jscore-core cover 95% of common moment use cases",
1391
+ detectionPattern: `from ['"]moment['"]|require\\(['"]moment['"]\\)`
1392
+ },
1393
+ {
1394
+ name: "date-fns",
1395
+ size: "1.2 MB (tree-shaked ~50KB)",
1396
+ replacement: "jscore-core/date",
1397
+ confidence: "medium",
1398
+ autoPrReady: false,
1399
+ reason: "Partially overlapping \u2014 jscore-core covers basic date ops but not all locale support",
1400
+ detectionPattern: `from ['"]date-fns['"]|require\\(['"]date-fns['"]\\)`
1401
+ },
1402
+ {
1403
+ name: "axios",
1404
+ size: "1.6 MB",
1405
+ replacement: "native fetch + jscore-core/async/retry",
1406
+ confidence: "medium",
1407
+ autoPrReady: false,
1408
+ reason: "Native fetch covers most use cases; needs manual review for interceptors",
1409
+ detectionPattern: `from ['"]axios['"]|require\\(['"]axios['"]\\)`
1410
+ },
1411
+ {
1412
+ name: "uuid",
1413
+ size: "30 KB",
1414
+ replacement: "crypto.randomUUID() (native)",
1415
+ confidence: "high",
1416
+ autoPrReady: true,
1417
+ reason: "crypto.randomUUID() is available in all modern Node.js and browsers",
1418
+ detectionPattern: `from ['"]uuid['"]|require\\(['"]uuid['"]\\)`
1419
+ },
1420
+ {
1421
+ name: "deepmerge",
1422
+ size: "15 KB",
1423
+ replacement: "jscore-core",
1424
+ confidence: "high",
1425
+ autoPrReady: true,
1426
+ reason: "jscore-core provides deepMerge out of the box",
1427
+ detectionPattern: `from ['"]deepmerge['"]|require\\(['"]deepmerge['"]\\)`
1428
+ },
1429
+ {
1430
+ name: "lodash.merge",
1431
+ size: "25 KB",
1432
+ replacement: "jscore-core",
1433
+ confidence: "high",
1434
+ autoPrReady: true,
1435
+ reason: "jscore-core provides deepMerge out of the box",
1436
+ detectionPattern: `from ['"]lodash\\.(merge|clone|pick|omit|get|set)['"]`
1437
+ },
1438
+ {
1439
+ name: "chalk",
1440
+ size: "45 KB",
1441
+ replacement: "picocolors",
1442
+ confidence: "medium",
1443
+ autoPrReady: false,
1444
+ reason: "picocolors is 3KB vs chalk's 45KB with same API",
1445
+ detectionPattern: `from ['"]chalk['"]|require\\(['"]chalk['"]\\)`
1446
+ },
1447
+ {
1448
+ name: "nanoid",
1449
+ size: "8 KB",
1450
+ replacement: "jscore-core/string (nanoid)",
1451
+ confidence: "high",
1452
+ autoPrReady: true,
1453
+ reason: "jscore-core provides nanoid with same API",
1454
+ detectionPattern: `from ['"]nanoid['"]|require\\(['"]nanoid['"]\\)`
1455
+ },
1456
+ {
1457
+ name: "dayjs",
1458
+ size: "50 KB",
1459
+ replacement: "jscore-core/date",
1460
+ confidence: "medium",
1461
+ autoPrReady: false,
1462
+ reason: "Partially overlapping \u2014 covers basics but not all plugins",
1463
+ detectionPattern: `from ['"]dayjs['"]|require\\(['"]dayjs['"]\\)`
1464
+ },
1465
+ {
1466
+ name: "clsx",
1467
+ size: "5 KB",
1468
+ replacement: "native template literals",
1469
+ confidence: "high",
1470
+ autoPrReady: true,
1471
+ reason: "Can be replaced with simple template literal conditional pattern",
1472
+ detectionPattern: `from ['"]clsx['"]|require\\(['"]clsx['"]\\)`
1473
+ }
1474
+ ];
1475
+ var KNOWN_CVES = {
1476
+ "ansi-regex": [
1477
+ { cve: "CVE-2021-3807", severity: "high", fix: "Update to ansi-regex@6.0.1 or later" }
1478
+ ],
1479
+ "semver": [
1480
+ { cve: "CVE-2022-25883", severity: "medium", fix: "Update to semver@7.5.2 or later" }
1481
+ ],
1482
+ "json5": [
1483
+ { cve: "CVE-2022-46175", severity: "high", fix: "Update to json5@2.2.3 or later" }
1484
+ ],
1485
+ "lodash": [
1486
+ { cve: "CVE-2020-28502", severity: "high", fix: "Update to lodash@4.17.21 or later" },
1487
+ { cve: "CVE-2020-8203", severity: "medium", fix: "Update to lodash@4.17.21 or later" }
1488
+ ]
1489
+ };
1490
+
1491
+ // src/dep-exray/scanner/index.ts
1492
+ function parsePackageJson(path) {
1493
+ const raw = readFileSync(path, "utf-8");
1494
+ const json = JSON.parse(raw);
1495
+ return {
1496
+ name: json.name ?? basename2(join2(path, "..")),
1497
+ dependencies: json.dependencies ?? {},
1498
+ devDependencies: json.devDependencies ?? {}
1499
+ };
1500
+ }
1501
+ function parseLockfile(projectPath) {
1502
+ const lockPath = join2(projectPath, "package-lock.json");
1503
+ if (!existsSync(lockPath)) return null;
1504
+ try {
1505
+ const raw = readFileSync(lockPath, "utf-8");
1506
+ const json = JSON.parse(raw);
1507
+ const packages = {};
1508
+ if (json.packages) {
1509
+ for (const [key, val] of Object.entries(json.packages)) {
1510
+ if (key) {
1511
+ packages[key] = val;
1512
+ }
1513
+ }
1514
+ }
1515
+ if (json.dependencies && Object.keys(packages).length === 0) {
1516
+ for (const [key, val] of Object.entries(json.dependencies)) {
1517
+ packages[key] = { version: val.version, dependencies: val.requires };
1518
+ }
1519
+ }
1520
+ return { packages };
1521
+ } catch {
1522
+ return null;
1523
+ }
1524
+ }
1525
+ function detectImportInFile(filePath, regex) {
1526
+ try {
1527
+ const content = readFileSync(filePath, "utf-8");
1528
+ return regex.test(content);
1529
+ } catch {
1530
+ return false;
1531
+ }
1532
+ }
1533
+ function detectImportsInSrc(projectPath, mapping) {
1534
+ const regex = new RegExp(mapping.detectionPattern);
1535
+ const srcDir = join2(projectPath, "src");
1536
+ if (!existsSync(srcDir)) return false;
1537
+ try {
1538
+ const files = collectSourceFiles(srcDir);
1539
+ for (const file of files) {
1540
+ if (detectImportInFile(file, regex)) return true;
1541
+ }
1542
+ } catch {
1543
+ return false;
1544
+ }
1545
+ return false;
1546
+ }
1547
+ function collectSourceFiles(dir) {
1548
+ const results = [];
1549
+ try {
1550
+ const entries = readdirSync(dir, { withFileTypes: true });
1551
+ for (const entry of entries) {
1552
+ if (entry.name === "node_modules" || entry.name === "dist" || entry.name === ".git" || entry.name === "coverage" || entry.name === ".tsup") {
1553
+ continue;
1554
+ }
1555
+ const full = join2(dir, entry.name);
1556
+ if (entry.isDirectory()) {
1557
+ results.push(...collectSourceFiles(full));
1558
+ } else if (entry.isFile()) {
1559
+ const ext = entry.name.split(".").pop();
1560
+ if (ext === "ts" || ext === "tsx" || ext === "js" || ext === "jsx" || ext === "mjs" || ext === "cjs") {
1561
+ results.push(full);
1562
+ }
1563
+ }
1564
+ }
1565
+ } catch {
1566
+ }
1567
+ return results;
1568
+ }
1569
+ function estimateTransitiveCount(lockfile, directNames) {
1570
+ if (!lockfile) return 0;
1571
+ let count = 0;
1572
+ for (const key of Object.keys(lockfile.packages)) {
1573
+ if (!key || key === "") continue;
1574
+ const name = key.startsWith("node_modules/") ? key.slice("node_modules/".length) : key;
1575
+ const rootName = name.startsWith("@") ? `${name.split("/")[0]}/${name.split("/")[1]}` : name.split("/")[0];
1576
+ if (rootName && !directNames.has(rootName)) {
1577
+ count++;
1578
+ }
1579
+ }
1580
+ return count;
1581
+ }
1582
+ function parseSize(value) {
1583
+ const cleaned = value.replace(/\(.*?\)/g, "").trim();
1584
+ const match = cleaned.match(/^([\d.]+)\s*(KB|MB)/i);
1585
+ if (!match) return 0;
1586
+ const num = Number.parseFloat(match[1]);
1587
+ if (!match[2]) return 0;
1588
+ if (match[2].toUpperCase() === "MB") return num * 1024;
1589
+ return num;
1590
+ }
1591
+ async function scanProject(config) {
1592
+ const projectPath = config.path ?? ".";
1593
+ const pkgPath = join2(projectPath, "package.json");
1594
+ if (!existsSync(pkgPath)) {
1595
+ throw new Error(`No package.json found at ${projectPath}. Run dep-exray in a JavaScript/TypeScript project directory.`);
1596
+ }
1597
+ const pkg = parsePackageJson(pkgPath);
1598
+ const lockfile = parseLockfile(projectPath);
1599
+ const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
1600
+ const directNames = new Set(Object.keys(allDeps));
1601
+ const transitiveCount = estimateTransitiveCount(lockfile, directNames);
1602
+ const highImpactReplacements = [];
1603
+ const mediumImpactReplacements = [];
1604
+ const securityIssues = [];
1605
+ const sizeMap = {};
1606
+ for (const m of KNOWN_MAPPINGS) {
1607
+ sizeMap[m.name] = m.size;
1608
+ }
1609
+ for (const mapping of KNOWN_MAPPINGS) {
1610
+ const isDirect = directNames.has(mapping.name);
1611
+ if (!isDirect) continue;
1612
+ const isUsed = detectImportsInSrc(projectPath, mapping);
1613
+ if (!isUsed && !config.verbose) continue;
1614
+ const mappingSize = parseSize(sizeMap[mapping.name] ?? "0 KB");
1615
+ const replacementSize = mapping.replacement.startsWith("native") ? 0 : 5;
1616
+ const reductionStr = mappingSize > 1024 ? `${(mappingSize / 1024).toFixed(1)} MB \u2192 ${replacementSize} KB` : `${mappingSize.toFixed(0)} KB \u2192 ${replacementSize} KB`;
1617
+ const suggestion = {
1618
+ packageName: mapping.name,
1619
+ reason: mapping.reason,
1620
+ replacement: mapping.replacement,
1621
+ estimatedSizeReduction: reductionStr,
1622
+ confidence: mapping.confidence,
1623
+ autoPrReady: mapping.autoPrReady
1624
+ };
1625
+ if (mapping.confidence === "high") {
1626
+ highImpactReplacements.push(suggestion);
1627
+ } else {
1628
+ mediumImpactReplacements.push(suggestion);
1629
+ }
1630
+ }
1631
+ for (const [name, cves] of Object.entries(KNOWN_CVES)) {
1632
+ if (directNames.has(name)) {
1633
+ for (const cveItem of cves) {
1634
+ securityIssues.push({
1635
+ packageName: name,
1636
+ cveId: cveItem.cve,
1637
+ severity: cveItem.severity,
1638
+ fix: cveItem.fix
1639
+ });
1640
+ }
1641
+ }
1642
+ }
1643
+ let totalSizeKB = 0;
1644
+ for (const depName of directNames) {
1645
+ const sizeStr = sizeMap[depName];
1646
+ if (sizeStr) {
1647
+ totalSizeKB += parseSize(sizeStr);
1648
+ } else {
1649
+ totalSizeKB += 50;
1650
+ }
1651
+ }
1652
+ totalSizeKB += transitiveCount * 30;
1653
+ const totalSizeStr = totalSizeKB > 1024 ? `${(totalSizeKB / 1024).toFixed(1)} MB` : `${totalSizeKB.toFixed(0)} KB`;
1654
+ return {
1655
+ projectName: pkg.name,
1656
+ directDeps: directNames.size,
1657
+ transitiveDeps: transitiveCount,
1658
+ totalEstimatedSize: totalSizeStr,
1659
+ highImpactReplacements,
1660
+ mediumImpactReplacements,
1661
+ securityIssues
1662
+ };
1663
+ }
1664
+
1665
+ // src/dep-exray/reporter/index.ts
1666
+ import pc from "picocolors";
1667
+ function severityColor(severity) {
1668
+ switch (severity) {
1669
+ case "critical":
1670
+ return pc.bold(pc.red(severity.toUpperCase()));
1671
+ case "high":
1672
+ return pc.red(severity.toUpperCase());
1673
+ case "medium":
1674
+ return pc.yellow(severity.toUpperCase());
1675
+ case "low":
1676
+ return pc.dim(severity.toUpperCase());
1677
+ }
1678
+ }
1679
+ function confidenceIcon(confidence) {
1680
+ switch (confidence) {
1681
+ case "high":
1682
+ return pc.green("\u25CF");
1683
+ case "medium":
1684
+ return pc.yellow("\u25CF");
1685
+ case "low":
1686
+ return pc.red("\u25CF");
1687
+ }
1688
+ }
1689
+ function generateReport(result, jsonOutput) {
1690
+ if (jsonOutput) {
1691
+ return JSON.stringify(result, null, 2);
1692
+ }
1693
+ const lines = [];
1694
+ lines.push(pc.bold(pc.cyan(`\u250C${"\u2500".repeat(58)}\u2510`)));
1695
+ lines.push(pc.bold(pc.cyan(`\u2502${" ".repeat(18)}dep-exray Report${" ".repeat(21)}\u2502`)));
1696
+ lines.push(pc.bold(pc.cyan(`\u251C${"\u2500".repeat(58)}\u2524`)));
1697
+ lines.push(pc.bold(pc.cyan(`\u2502 ${pc.white("\u{1F4E6} PROJECT:")} ${pc.bold(result.projectName)}${" ".repeat(Math.max(1, 47 - result.projectName.length))}\u2502`)));
1698
+ lines.push(pc.bold(pc.cyan(`\u2502 ${pc.white("\u{1F4CA} DEPENDENCIES:")} ${pc.bold(String(result.directDeps))} direct + ${pc.bold(String(result.transitiveDeps))} transitive${" ".repeat(Math.max(1, 27 - String(result.transitiveDeps).length))}\u2502`)));
1699
+ lines.push(pc.bold(pc.cyan(`\u2502 ${pc.white("\u{1F4BE} TOTAL SIZE:")} ${pc.bold(result.totalEstimatedSize)}${" ".repeat(Math.max(1, 42 - result.totalEstimatedSize.length))}\u2502`)));
1700
+ lines.push(pc.bold(pc.cyan(`\u251C${"\u2500".repeat(58)}\u2524`)));
1701
+ if (result.highImpactReplacements.length > 0) {
1702
+ lines.push(pc.bold(pc.cyan(`\u2502 ${pc.green("\u{1F7E2}")} ${pc.bold(pc.green("HIGH IMPACT REPLACEMENTS"))}${" ".repeat(23)}\u2502`)));
1703
+ for (const item of result.highImpactReplacements) {
1704
+ const autoPr = item.autoPrReady ? pc.green("\u2713 Auto-PR ready") : pc.dim("Manual review needed");
1705
+ const confIcon = confidenceIcon(item.confidence);
1706
+ lines.push(pc.bold(pc.cyan(`\u251C${"\u2500".repeat(58)}\u2524`)));
1707
+ lines.push(pc.bold(pc.cyan(`\u2502 ${pc.red("\u2717")} ${pc.bold(item.packageName)} (${item.estimatedSizeReduction})${" ".repeat(Math.max(1, 38 - item.estimatedSizeReduction.length))}\u2502`)));
1708
+ lines.push(pc.bold(pc.cyan(`\u2502 ${pc.dim("\u2192")} ${pc.cyan(item.replacement)}${" ".repeat(Math.max(1, 51 - item.replacement.length))}\u2502`)));
1709
+ lines.push(pc.bold(pc.cyan(`\u2502 ${pc.dim("\u2514\u2500")} ${autoPr} ${confIcon} ${item.confidence}${" ".repeat(Math.max(1, 35))}\u2502`)));
1710
+ }
1711
+ }
1712
+ if (result.mediumImpactReplacements.length > 0) {
1713
+ lines.push(pc.bold(pc.cyan(`\u251C${"\u2500".repeat(58)}\u2524`)));
1714
+ lines.push(pc.bold(pc.cyan(`\u2502 ${pc.yellow("\u{1F7E1}")} ${pc.bold(pc.yellow("MEDIUM IMPACT REPLACEMENTS"))}${" ".repeat(20)}\u2502`)));
1715
+ for (const item of result.mediumImpactReplacements) {
1716
+ const autoPr = item.autoPrReady ? pc.green("\u2713 Auto-PR ready") : pc.dim("Manual review needed");
1717
+ const confIcon = confidenceIcon(item.confidence);
1718
+ lines.push(pc.bold(pc.cyan(`\u251C${"\u2500".repeat(58)}\u2524`)));
1719
+ lines.push(pc.bold(pc.cyan(`\u2502 ${pc.red("\u2717")} ${pc.bold(item.packageName)} (${item.estimatedSizeReduction})${" ".repeat(Math.max(1, 38 - item.estimatedSizeReduction.length))}\u2502`)));
1720
+ lines.push(pc.bold(pc.cyan(`\u2502 ${pc.dim("\u2192")} ${pc.cyan(item.replacement)}${" ".repeat(Math.max(1, 51 - item.replacement.length))}\u2502`)));
1721
+ lines.push(pc.bold(pc.cyan(`\u2502 ${pc.dim("\u2514\u2500")} ${autoPr} ${confIcon} ${item.confidence}${" ".repeat(Math.max(1, 35))}\u2502`)));
1722
+ }
1723
+ }
1724
+ if (result.securityIssues.length > 0) {
1725
+ lines.push(pc.bold(pc.cyan(`\u251C${"\u2500".repeat(58)}\u2524`)));
1726
+ lines.push(pc.bold(pc.cyan(`\u2502 ${pc.red("\u{1F534}")} ${pc.bold(pc.red("SECURITY ISSUES"))}${" ".repeat(33)}\u2502`)));
1727
+ for (const issue of result.securityIssues) {
1728
+ lines.push(pc.bold(pc.cyan(`\u251C${"\u2500".repeat(58)}\u2524`)));
1729
+ lines.push(pc.bold(pc.cyan(`\u2502 ${severityColor(issue.severity)} ${pc.bold(issue.cveId)} in ${issue.packageName}${" ".repeat(Math.max(1, 40 - issue.packageName.length))}\u2502`)));
1730
+ lines.push(pc.bold(pc.cyan(`\u2502 ${pc.dim("\u2192")} ${issue.fix}${" ".repeat(Math.max(1, 52 - issue.fix.length))}\u2502`)));
1731
+ }
1732
+ }
1733
+ lines.push(pc.bold(pc.cyan(`\u2514${"\u2500".repeat(58)}\u2518`)));
1734
+ return lines.join("\n");
1735
+ }
1736
+
1737
+ // src/dep-exray/analyzer/index.ts
1738
+ import { readFileSync as readFileSync2 } from "fs";
1739
+ import { readdirSync as readdirSync2, statSync } from "fs";
1740
+ import { join as join3, extname as extname2 } from "path";
1741
+ var SOURCE_EXTENSIONS = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"]);
1742
+ function isSourceFile(file) {
1743
+ return SOURCE_EXTENSIONS.has(extname2(file));
1744
+ }
1745
+ function collectSourceFiles2(dir) {
1746
+ const results = [];
1747
+ try {
1748
+ const entries = readdirSync2(dir, { withFileTypes: true });
1749
+ for (const entry of entries) {
1750
+ const fullPath = join3(dir, entry.name);
1751
+ if (entry.name === "node_modules" || entry.name === "dist" || entry.name === ".git" || entry.name === "coverage" || entry.name === ".tsup") {
1752
+ continue;
1753
+ }
1754
+ if (entry.isDirectory()) {
1755
+ results.push(...collectSourceFiles2(fullPath));
1756
+ } else if (entry.isFile() && isSourceFile(entry.name)) {
1757
+ results.push(fullPath);
1758
+ }
1759
+ }
1760
+ } catch {
1761
+ }
1762
+ return results;
1763
+ }
1764
+ function escapeRegex(str) {
1765
+ return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1766
+ }
1767
+ async function analyzeUsage(projectPath, packageName) {
1768
+ const importLocations = [];
1769
+ const escaped = escapeRegex(packageName);
1770
+ const importRegex = new RegExp(
1771
+ `(?:from\\s+['"]${escaped}(?:/['"]|['"])|require\\(\\s*['"]${escaped}(?:/|['"]))`,
1772
+ "g"
1773
+ );
1774
+ const srcDir = join3(projectPath, "src");
1775
+ let files;
1776
+ try {
1777
+ if (statSync(srcDir).isDirectory()) {
1778
+ files = collectSourceFiles2(srcDir);
1779
+ } else {
1780
+ files = collectSourceFiles2(projectPath);
1781
+ }
1782
+ } catch {
1783
+ files = collectSourceFiles2(projectPath);
1784
+ }
1785
+ for (const file of files) {
1786
+ try {
1787
+ const content = readFileSync2(file, "utf-8");
1788
+ importRegex.lastIndex = 0;
1789
+ if (importRegex.test(content)) {
1790
+ importLocations.push(file);
1791
+ }
1792
+ } catch {
1793
+ }
1794
+ }
1795
+ return {
1796
+ isUsed: importLocations.length > 0,
1797
+ importCount: importLocations.length,
1798
+ importLocations
1799
+ };
1800
+ }
1368
1801
  export {
1369
1802
  DivisionByZeroError,
1370
1803
  InvalidDateError,
1804
+ KNOWN_CVES,
1805
+ KNOWN_MAPPINGS,
1371
1806
  add,
1372
1807
  addBusinessDays,
1373
1808
  addDays,
1374
1809
  addMonths,
1375
1810
  addYears,
1376
1811
  allSettledMap,
1812
+ analyzeUsage,
1377
1813
  approxEqual,
1378
1814
  assertDefined,
1379
1815
  assertType,
@@ -1413,6 +1849,7 @@ export {
1413
1849
  format,
1414
1850
  formatDate,
1415
1851
  generateOTP,
1852
+ generateReport,
1416
1853
  generateToken,
1417
1854
  getType2 as getType,
1418
1855
  groupBy,
@@ -1476,6 +1913,7 @@ export {
1476
1913
  safeJsonParse,
1477
1914
  sample,
1478
1915
  sampleSize,
1916
+ scanProject,
1479
1917
  shuffle,
1480
1918
  simpleHash,
1481
1919
  sleep,