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/async/index.js.map +1 -1
- package/dist/collection/index.js.map +1 -1
- package/dist/core/index.js.map +1 -1
- package/dist/crypto/index.js.map +1 -1
- package/dist/date/index.js.map +1 -1
- package/dist/dep-exray/analyzer/index.d.ts +7 -0
- package/dist/dep-exray/analyzer/index.js +68 -0
- package/dist/dep-exray/analyzer/index.js.map +1 -0
- package/dist/dep-exray/cli.d.ts +1 -0
- package/dist/dep-exray/cli.js +389 -0
- package/dist/dep-exray/cli.js.map +1 -0
- package/dist/dep-exray/index.d.ts +5 -0
- package/dist/dep-exray/index.js +440 -0
- package/dist/dep-exray/index.js.map +1 -0
- package/dist/dep-exray/known-mappings.d.ts +17 -0
- package/dist/dep-exray/known-mappings.js +122 -0
- package/dist/dep-exray/known-mappings.js.map +1 -0
- package/dist/dep-exray/reporter/index.d.ts +5 -0
- package/dist/dep-exray/reporter/index.js +75 -0
- package/dist/dep-exray/reporter/index.js.map +1 -0
- package/dist/dep-exray/scanner/index.d.ts +5 -0
- package/dist/dep-exray/scanner/index.js +299 -0
- package/dist/dep-exray/scanner/index.js.map +1 -0
- package/dist/dep-exray/types.d.ts +38 -0
- package/dist/dep-exray/types.js +1 -0
- package/dist/dep-exray/types.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +438 -0
- package/dist/index.js.map +1 -1
- package/dist/io/index.js.map +1 -1
- package/dist/math/index.js.map +1 -1
- package/dist/path/index.js.map +1 -1
- package/dist/string/index.js.map +1 -1
- package/dist/type/index.js.map +1 -1
- package/package.json +44 -4
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,
|