hermex 0.0.1-beta.1 → 1.1.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 +141 -145
- package/dist/cli.js +487 -246
- package/dist/cli.js.map +1 -1
- package/package.json +20 -23
package/dist/cli.js
CHANGED
|
@@ -1,22 +1,27 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
1
|
'use strict';
|
|
3
2
|
|
|
4
3
|
var commander = require('commander');
|
|
5
|
-
var glob = require('glob');
|
|
6
4
|
var ora = require('ora');
|
|
7
|
-
var
|
|
5
|
+
var chalk6 = require('chalk');
|
|
8
6
|
var core = require('@swc/core');
|
|
9
|
-
var
|
|
10
|
-
var path = require('path');
|
|
7
|
+
var fs3 = require('fs');
|
|
11
8
|
var Table = require('cli-table3');
|
|
9
|
+
var glob = require('glob');
|
|
10
|
+
var path = require('path');
|
|
11
|
+
var yaml = require('js-yaml');
|
|
12
|
+
var lockfile = require('@yarnpkg/lockfile');
|
|
12
13
|
|
|
13
14
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
14
15
|
|
|
15
16
|
var ora__default = /*#__PURE__*/_interopDefault(ora);
|
|
16
|
-
var
|
|
17
|
-
var
|
|
18
|
-
var path__default = /*#__PURE__*/_interopDefault(path);
|
|
17
|
+
var chalk6__default = /*#__PURE__*/_interopDefault(chalk6);
|
|
18
|
+
var fs3__default = /*#__PURE__*/_interopDefault(fs3);
|
|
19
19
|
var Table__default = /*#__PURE__*/_interopDefault(Table);
|
|
20
|
+
var path__default = /*#__PURE__*/_interopDefault(path);
|
|
21
|
+
var yaml__default = /*#__PURE__*/_interopDefault(yaml);
|
|
22
|
+
var lockfile__default = /*#__PURE__*/_interopDefault(lockfile);
|
|
23
|
+
|
|
24
|
+
// src/cli.ts
|
|
20
25
|
|
|
21
26
|
// src/swc-parser/core/state.ts
|
|
22
27
|
function createState() {
|
|
@@ -699,7 +704,7 @@ function parseFile(filePath, options = {}) {
|
|
|
699
704
|
console.log(`
|
|
700
705
|
\u{1F4C1} Analyzing: ${filePath}`);
|
|
701
706
|
try {
|
|
702
|
-
const code =
|
|
707
|
+
const code = fs3__default.default.readFileSync(filePath, "utf8");
|
|
703
708
|
return parseCode(code, options);
|
|
704
709
|
} catch (error) {
|
|
705
710
|
console.error(`\u274C Error parsing ${filePath}:`, error.message);
|
|
@@ -744,11 +749,12 @@ function filterReportByLibrary(report, libraryName) {
|
|
|
744
749
|
}
|
|
745
750
|
|
|
746
751
|
// src/utils/aggregator.ts
|
|
747
|
-
function aggregateReports(reports) {
|
|
752
|
+
function aggregateReports(reports, versions = {}) {
|
|
748
753
|
const componentUsageMap = /* @__PURE__ */ new Map();
|
|
749
754
|
let totalImports = 0;
|
|
750
755
|
let totalUsagePatterns = 0;
|
|
751
756
|
const patternCountMap = /* @__PURE__ */ new Map();
|
|
757
|
+
const availablePackages = Object.keys(versions);
|
|
752
758
|
for (const report of reports) {
|
|
753
759
|
totalImports += report.summary.totalImports;
|
|
754
760
|
totalUsagePatterns += report.summary.totalUsagePatterns;
|
|
@@ -758,7 +764,11 @@ function aggregateReports(reports) {
|
|
|
758
764
|
if (existing) {
|
|
759
765
|
existing.count++;
|
|
760
766
|
} else {
|
|
761
|
-
const source = findComponentSource(
|
|
767
|
+
const source = findComponentSource(
|
|
768
|
+
jsx.component,
|
|
769
|
+
report,
|
|
770
|
+
availablePackages
|
|
771
|
+
);
|
|
762
772
|
componentUsageMap.set(key, {
|
|
763
773
|
name: jsx.component,
|
|
764
774
|
source,
|
|
@@ -778,6 +788,10 @@ function aggregateReports(reports) {
|
|
|
778
788
|
displayName: getPatternDisplayName(type),
|
|
779
789
|
count
|
|
780
790
|
})).sort((a, b) => b.count - a.count);
|
|
791
|
+
const packageDistribution = calculatePackageDistribution(
|
|
792
|
+
componentUsageMap,
|
|
793
|
+
versions
|
|
794
|
+
);
|
|
781
795
|
return {
|
|
782
796
|
filesAnalyzed: reports.length,
|
|
783
797
|
totalImports,
|
|
@@ -787,22 +801,49 @@ function aggregateReports(reports) {
|
|
|
787
801
|
componentUsage: componentUsageMap,
|
|
788
802
|
topComponents,
|
|
789
803
|
allComponents,
|
|
804
|
+
packageDistribution,
|
|
790
805
|
reports
|
|
791
806
|
};
|
|
792
807
|
}
|
|
793
|
-
function
|
|
808
|
+
function resolvePackageFromImportPath(importPath, availablePackages) {
|
|
809
|
+
if (importPath.startsWith(".") || importPath.startsWith("/")) {
|
|
810
|
+
return "local";
|
|
811
|
+
}
|
|
812
|
+
const sortedPackages = [...availablePackages].sort(
|
|
813
|
+
(a, b) => b.length - a.length
|
|
814
|
+
);
|
|
815
|
+
for (const pkg of sortedPackages) {
|
|
816
|
+
if (importPath === pkg) {
|
|
817
|
+
return pkg;
|
|
818
|
+
}
|
|
819
|
+
if (importPath.startsWith(`${pkg}/`)) {
|
|
820
|
+
return pkg;
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
return "unknown";
|
|
824
|
+
}
|
|
825
|
+
function findComponentSource(componentName, report, availablePackages) {
|
|
794
826
|
const namedImport = report.patterns.imports.named.find(
|
|
795
827
|
(imp) => imp.name === componentName
|
|
796
828
|
);
|
|
797
|
-
if (namedImport)
|
|
829
|
+
if (namedImport)
|
|
830
|
+
return resolvePackageFromImportPath(namedImport.source, availablePackages);
|
|
798
831
|
const defaultImport = report.patterns.imports.default.find(
|
|
799
832
|
(imp) => imp.name === componentName
|
|
800
833
|
);
|
|
801
|
-
if (defaultImport)
|
|
834
|
+
if (defaultImport)
|
|
835
|
+
return resolvePackageFromImportPath(
|
|
836
|
+
defaultImport.source,
|
|
837
|
+
availablePackages
|
|
838
|
+
);
|
|
802
839
|
const aliasedImport = report.patterns.imports.aliased.find(
|
|
803
840
|
(imp) => imp.local === componentName
|
|
804
841
|
);
|
|
805
|
-
if (aliasedImport)
|
|
842
|
+
if (aliasedImport)
|
|
843
|
+
return resolvePackageFromImportPath(
|
|
844
|
+
aliasedImport.source,
|
|
845
|
+
availablePackages
|
|
846
|
+
);
|
|
806
847
|
return "unknown";
|
|
807
848
|
}
|
|
808
849
|
function countPatterns(report, patternMap) {
|
|
@@ -883,127 +924,115 @@ function getPatternDisplayName(patternType) {
|
|
|
883
924
|
};
|
|
884
925
|
return displayNames[patternType] || patternType;
|
|
885
926
|
}
|
|
886
|
-
function
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
}
|
|
904
|
-
for (const imp of report.patterns.imports.namespace) {
|
|
905
|
-
console.log(
|
|
906
|
-
chalk__default.default.gray(
|
|
907
|
-
`[VERBOSE] Found namespace import: ${imp.name} from ${imp.source}`
|
|
908
|
-
)
|
|
909
|
-
);
|
|
910
|
-
}
|
|
911
|
-
for (const imp of report.patterns.imports.aliased) {
|
|
912
|
-
console.log(
|
|
913
|
-
chalk__default.default.gray(
|
|
914
|
-
`[VERBOSE] Found aliased import: ${imp.imported} as ${imp.local} from ${imp.source}`
|
|
915
|
-
)
|
|
916
|
-
);
|
|
917
|
-
}
|
|
918
|
-
for (const obj of report.patterns.usage.objects) {
|
|
919
|
-
for (const mapping of obj.mappings) {
|
|
920
|
-
console.log(
|
|
921
|
-
chalk__default.default.gray(
|
|
922
|
-
`[VERBOSE] Found Object mapping with Component: ${mapping.component}`
|
|
923
|
-
)
|
|
924
|
-
);
|
|
927
|
+
function getPackageVersion(packageName, versions) {
|
|
928
|
+
if (versions[packageName]) {
|
|
929
|
+
return versions[packageName];
|
|
930
|
+
}
|
|
931
|
+
if (packageName.includes("/")) {
|
|
932
|
+
const parts = packageName.split("/");
|
|
933
|
+
if (packageName.startsWith("@") && parts.length > 2) {
|
|
934
|
+
const basePackage = `${parts[0]}/${parts[1]}`;
|
|
935
|
+
if (versions[basePackage]) {
|
|
936
|
+
return versions[basePackage];
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
if (!packageName.startsWith("@") && parts.length > 1) {
|
|
940
|
+
const basePackage = parts[0];
|
|
941
|
+
if (versions[basePackage]) {
|
|
942
|
+
return versions[basePackage];
|
|
943
|
+
}
|
|
925
944
|
}
|
|
926
945
|
}
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
chalk__default.default.gray(
|
|
949
|
-
`[VERBOSE] Found Destructuring: ${destructure.property} from ${destructure.source}`
|
|
950
|
-
)
|
|
951
|
-
);
|
|
952
|
-
}
|
|
953
|
-
for (const lazy of report.patterns.advanced.lazy) {
|
|
954
|
-
console.log(chalk__default.default.gray(`[VERBOSE] Found Lazy import: ${lazy.source}`));
|
|
955
|
-
}
|
|
956
|
-
for (const dynamic of report.patterns.advanced.dynamic) {
|
|
957
|
-
console.log(
|
|
958
|
-
chalk__default.default.gray(`[VERBOSE] Found Dynamic import: ${dynamic.source}`)
|
|
959
|
-
);
|
|
960
|
-
}
|
|
961
|
-
for (const hoc of report.patterns.advanced.hoc) {
|
|
962
|
-
console.log(
|
|
963
|
-
chalk__default.default.gray(`[VERBOSE] Found HOC: ${hoc.function}(${hoc.component})`)
|
|
964
|
-
);
|
|
965
|
-
}
|
|
966
|
-
for (const memo of report.patterns.advanced.memo) {
|
|
967
|
-
console.log(
|
|
968
|
-
chalk__default.default.gray(`[VERBOSE] Found Memoized component: ${memo.component}`)
|
|
969
|
-
);
|
|
970
|
-
}
|
|
971
|
-
if (report.patterns.advanced.forwardRef.length > 0) {
|
|
972
|
-
console.log(chalk__default.default.gray(`[VERBOSE] Found Forward Ref usage`));
|
|
946
|
+
return null;
|
|
947
|
+
}
|
|
948
|
+
function calculatePackageDistribution(componentUsageMap, versions) {
|
|
949
|
+
const packageMap = /* @__PURE__ */ new Map();
|
|
950
|
+
for (const component of componentUsageMap.values()) {
|
|
951
|
+
if (component.source === "unknown") continue;
|
|
952
|
+
const existing = packageMap.get(component.source);
|
|
953
|
+
if (existing) {
|
|
954
|
+
existing.componentCount++;
|
|
955
|
+
existing.usageCount += component.count;
|
|
956
|
+
existing.components.push(component.name);
|
|
957
|
+
} else {
|
|
958
|
+
packageMap.set(component.source, {
|
|
959
|
+
packageName: component.source,
|
|
960
|
+
version: getPackageVersion(component.source, versions),
|
|
961
|
+
componentCount: 1,
|
|
962
|
+
usageCount: component.count,
|
|
963
|
+
percentage: 0,
|
|
964
|
+
components: [component.name]
|
|
965
|
+
});
|
|
966
|
+
}
|
|
973
967
|
}
|
|
974
|
-
|
|
975
|
-
|
|
968
|
+
const distribution = Array.from(packageMap.values());
|
|
969
|
+
const totalExternalUsage = distribution.reduce(
|
|
970
|
+
(sum, pkg) => sum + pkg.usageCount,
|
|
971
|
+
0
|
|
972
|
+
);
|
|
973
|
+
for (const pkg of distribution) {
|
|
974
|
+
pkg.percentage = totalExternalUsage > 0 ? pkg.usageCount / totalExternalUsage * 100 : 0;
|
|
976
975
|
}
|
|
977
|
-
|
|
976
|
+
return distribution.sort((a, b) => b.usageCount - a.usageCount);
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
// src/utils/format-utils.ts
|
|
980
|
+
function formatCount(num) {
|
|
981
|
+
return num.toLocaleString();
|
|
982
|
+
}
|
|
983
|
+
function formatDuration(seconds) {
|
|
984
|
+
return `${seconds.toFixed(1)}s`;
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
// src/utils/print-summary.ts
|
|
988
|
+
function printHeader() {
|
|
989
|
+
console.log(chalk6__default.default.green.bold("\n\u{1F4CA} Summary\n"));
|
|
978
990
|
}
|
|
979
991
|
function printSummary(aggregated, elapsedTimeSeconds) {
|
|
980
992
|
console.log(
|
|
981
|
-
|
|
982
|
-
`
|
|
993
|
+
chalk6__default.default.green(
|
|
994
|
+
`Analysis completed successfully in ${formatDuration(elapsedTimeSeconds)}
|
|
995
|
+
`
|
|
983
996
|
)
|
|
984
997
|
);
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
998
|
+
printHeader();
|
|
999
|
+
const table = new Table__default.default({
|
|
1000
|
+
head: ["Metric", "Count"],
|
|
1001
|
+
style: {
|
|
1002
|
+
head: ["cyan"],
|
|
1003
|
+
border: ["gray"]
|
|
1004
|
+
}
|
|
1005
|
+
});
|
|
1006
|
+
const externalComponents = aggregated.topComponents.filter(
|
|
1007
|
+
(comp) => comp.source !== "unknown" && comp.source !== "local"
|
|
1008
|
+
).length;
|
|
1009
|
+
const totalExternalUsage = aggregated.packageDistribution.reduce(
|
|
1010
|
+
(sum, pkg) => sum + pkg.usageCount,
|
|
1011
|
+
0
|
|
989
1012
|
);
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
)
|
|
1013
|
+
table.push(
|
|
1014
|
+
["Files Analyzed", formatCount(aggregated.filesAnalyzed)],
|
|
1015
|
+
["External Packages", formatCount(aggregated.packageDistribution.length)],
|
|
1016
|
+
["External Components", formatCount(externalComponents)],
|
|
1017
|
+
["Total Usages", formatCount(totalExternalUsage)]
|
|
994
1018
|
);
|
|
1019
|
+
console.log(table.toString());
|
|
1020
|
+
}
|
|
1021
|
+
function printHeader2() {
|
|
1022
|
+
console.log(chalk6__default.default.cyan.bold("\n\u{1F4CB} Details\n"));
|
|
995
1023
|
}
|
|
996
1024
|
function printDetails(aggregated) {
|
|
1025
|
+
printHeader2();
|
|
997
1026
|
console.log(
|
|
998
|
-
|
|
999
|
-
`
|
|
1027
|
+
chalk6__default.default.cyan(
|
|
1028
|
+
` Total usage patterns: ${aggregated.totalUsagePatterns.toLocaleString()}`
|
|
1000
1029
|
)
|
|
1001
1030
|
);
|
|
1002
1031
|
for (const pattern of aggregated.patternCounts) {
|
|
1003
1032
|
if (pattern.count > 0) {
|
|
1004
1033
|
console.log(
|
|
1005
|
-
|
|
1006
|
-
`
|
|
1034
|
+
chalk6__default.default.cyan(
|
|
1035
|
+
` ${pattern.displayName}: ${pattern.count.toLocaleString()}`
|
|
1007
1036
|
)
|
|
1008
1037
|
);
|
|
1009
1038
|
}
|
|
@@ -1017,12 +1046,12 @@ function renderBarChart(data, options = {}) {
|
|
|
1017
1046
|
emptyChar = "\u2591"
|
|
1018
1047
|
} = options;
|
|
1019
1048
|
if (data.length === 0) {
|
|
1020
|
-
console.log(
|
|
1049
|
+
console.log(chalk6__default.default.gray(" No data to display"));
|
|
1021
1050
|
return;
|
|
1022
1051
|
}
|
|
1023
1052
|
const maxValue = Math.max(...data.map((d) => d.value));
|
|
1024
1053
|
if (maxValue === 0) {
|
|
1025
|
-
console.log(
|
|
1054
|
+
console.log(chalk6__default.default.gray(" All values are zero"));
|
|
1026
1055
|
return;
|
|
1027
1056
|
}
|
|
1028
1057
|
const maxLabelLength = Math.max(...data.map((d) => d.label.length));
|
|
@@ -1031,154 +1060,357 @@ function renderBarChart(data, options = {}) {
|
|
|
1031
1060
|
const barLength = Math.round(percentage * maxWidth);
|
|
1032
1061
|
const emptyLength = maxWidth - barLength;
|
|
1033
1062
|
const paddedLabel = item.label.padEnd(maxLabelLength, " ");
|
|
1034
|
-
const bar =
|
|
1063
|
+
const bar = chalk6__default.default.green(barChar.repeat(barLength)) + chalk6__default.default.gray(emptyChar.repeat(emptyLength));
|
|
1035
1064
|
const valueStr = showValues ? ` ${item.value.toLocaleString()}` : "";
|
|
1036
1065
|
console.log(`${paddedLabel} ${bar}${valueStr}
|
|
1037
1066
|
`);
|
|
1038
1067
|
}
|
|
1039
1068
|
}
|
|
1040
1069
|
|
|
1041
|
-
// src/utils/print-
|
|
1042
|
-
function
|
|
1043
|
-
|
|
1044
|
-
if (mode === "log") {
|
|
1045
|
-
printTopComponentsLog(topComponents);
|
|
1046
|
-
} else if (mode === "table") {
|
|
1047
|
-
printTopComponentsTable(topComponents);
|
|
1048
|
-
} else if (mode === "chart") {
|
|
1049
|
-
printTopComponentsChart(topComponents);
|
|
1050
|
-
}
|
|
1070
|
+
// src/utils/print-components.ts
|
|
1071
|
+
function printHeader3() {
|
|
1072
|
+
console.log(chalk6__default.default.magenta.bold("\n\u269B\uFE0F Components\n"));
|
|
1051
1073
|
}
|
|
1052
|
-
function
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1074
|
+
function printComponents(aggregated, mode) {
|
|
1075
|
+
const components = aggregated.topComponents;
|
|
1076
|
+
if (mode === "table") {
|
|
1077
|
+
printComponentsTable(components);
|
|
1078
|
+
} else if (mode === "chart") {
|
|
1079
|
+
printComponentsChart(components);
|
|
1058
1080
|
}
|
|
1059
|
-
components.forEach((comp, idx) => {
|
|
1060
|
-
const rank = idx + 1;
|
|
1061
|
-
const emoji = rank === 1 ? "\u{1F947}" : rank === 2 ? "\u{1F948}" : rank === 3 ? "\u{1F949}" : " ";
|
|
1062
|
-
const sourceStr = comp.source !== "unknown" ? ` from ${comp.source}` : "";
|
|
1063
|
-
console.log(
|
|
1064
|
-
chalk__default.default.yellow(
|
|
1065
|
-
`[TOP-COMPONENTS] ${emoji} ${rank}. ${comp.name}${sourceStr}: ${comp.count} uses`
|
|
1066
|
-
)
|
|
1067
|
-
);
|
|
1068
|
-
});
|
|
1069
1081
|
}
|
|
1070
|
-
function
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1082
|
+
function printComponentsTable(components) {
|
|
1083
|
+
printHeader3();
|
|
1084
|
+
const externalComponents = components.filter(
|
|
1085
|
+
(comp) => comp.source !== "unknown" && comp.source !== "local"
|
|
1086
|
+
);
|
|
1087
|
+
if (externalComponents.length === 0) {
|
|
1088
|
+
console.log(chalk6__default.default.gray(" No external components found"));
|
|
1074
1089
|
return;
|
|
1075
1090
|
}
|
|
1076
1091
|
const table = new Table__default.default({
|
|
1077
|
-
head: ["
|
|
1092
|
+
head: ["Component", "Package", "Count"],
|
|
1078
1093
|
style: {
|
|
1079
1094
|
head: ["cyan"],
|
|
1080
1095
|
border: ["gray"]
|
|
1081
1096
|
}
|
|
1082
1097
|
});
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
const emoji = rank === 1 ? "\u{1F947}" : rank === 2 ? "\u{1F948}" : rank === 3 ? "\u{1F949}" : `${rank}.`;
|
|
1086
|
-
table.push([emoji, comp.name, comp.source, comp.count.toString()]);
|
|
1098
|
+
externalComponents.forEach((comp) => {
|
|
1099
|
+
table.push([comp.name, comp.source, comp.count.toString()]);
|
|
1087
1100
|
});
|
|
1088
1101
|
console.log(table.toString());
|
|
1089
1102
|
}
|
|
1090
|
-
function
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1103
|
+
function printComponentsChart(components) {
|
|
1104
|
+
printHeader3();
|
|
1105
|
+
const externalComponents = components.filter(
|
|
1106
|
+
(comp) => comp.source !== "unknown" && comp.source !== "local"
|
|
1107
|
+
);
|
|
1108
|
+
if (externalComponents.length === 0) {
|
|
1109
|
+
console.log(chalk6__default.default.gray(" No external components found"));
|
|
1094
1110
|
return;
|
|
1095
1111
|
}
|
|
1096
|
-
const data =
|
|
1112
|
+
const data = externalComponents.map((comp) => ({
|
|
1097
1113
|
label: comp.name,
|
|
1098
1114
|
value: comp.count
|
|
1099
1115
|
}));
|
|
1100
1116
|
renderBarChart(data, { maxWidth: 50 });
|
|
1101
1117
|
}
|
|
1102
|
-
function
|
|
1103
|
-
|
|
1118
|
+
function printHeader4() {
|
|
1119
|
+
console.log(chalk6__default.default.blue.bold("\n\u{1F50D} Code Patterns\n"));
|
|
1120
|
+
}
|
|
1121
|
+
function printPatterns(aggregated, mode) {
|
|
1122
|
+
const patterns = aggregated.patternCounts.filter((p) => p.count > 0);
|
|
1104
1123
|
if (mode === "table") {
|
|
1105
|
-
|
|
1124
|
+
printPatternsTable(patterns);
|
|
1106
1125
|
} else if (mode === "chart") {
|
|
1107
|
-
|
|
1126
|
+
printPatternsChart(patterns);
|
|
1108
1127
|
}
|
|
1109
1128
|
}
|
|
1110
|
-
function
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
if (components.length === 0) {
|
|
1115
|
-
console.log(chalk__default.default.gray(" No components found"));
|
|
1129
|
+
function printPatternsTable(patterns) {
|
|
1130
|
+
printHeader4();
|
|
1131
|
+
if (patterns.length === 0) {
|
|
1132
|
+
console.log(chalk6__default.default.gray(" No patterns found"));
|
|
1116
1133
|
return;
|
|
1117
1134
|
}
|
|
1118
1135
|
const table = new Table__default.default({
|
|
1119
|
-
head: ["
|
|
1136
|
+
head: ["Pattern", "Count"],
|
|
1120
1137
|
style: {
|
|
1121
1138
|
head: ["cyan"],
|
|
1122
1139
|
border: ["gray"]
|
|
1123
1140
|
}
|
|
1124
1141
|
});
|
|
1125
|
-
|
|
1126
|
-
table.push([
|
|
1142
|
+
patterns.forEach((pattern) => {
|
|
1143
|
+
table.push([pattern.displayName, pattern.count.toString()]);
|
|
1127
1144
|
});
|
|
1128
1145
|
console.log(table.toString());
|
|
1146
|
+
const totalPatterns = patterns.reduce((sum, p) => sum + p.count, 0);
|
|
1147
|
+
console.log(chalk6__default.default.gray(`
|
|
1148
|
+
Total: ${totalPatterns} patterns detected`));
|
|
1129
1149
|
}
|
|
1130
|
-
function
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
if (components.length === 0) {
|
|
1135
|
-
console.log(chalk__default.default.gray(" No components found"));
|
|
1150
|
+
function printPatternsChart(patterns) {
|
|
1151
|
+
printHeader4();
|
|
1152
|
+
if (patterns.length === 0) {
|
|
1153
|
+
console.log(chalk6__default.default.gray(" No patterns found"));
|
|
1136
1154
|
return;
|
|
1137
1155
|
}
|
|
1138
|
-
const data =
|
|
1139
|
-
label:
|
|
1140
|
-
value:
|
|
1156
|
+
const data = patterns.map((pattern) => ({
|
|
1157
|
+
label: pattern.displayName,
|
|
1158
|
+
value: pattern.count
|
|
1141
1159
|
}));
|
|
1142
1160
|
renderBarChart(data, { maxWidth: 50 });
|
|
1143
1161
|
}
|
|
1144
|
-
function
|
|
1145
|
-
|
|
1162
|
+
function printHeader5() {
|
|
1163
|
+
console.log(chalk6__default.default.blueBright.bold("\n\u{1F4E6} Packages\n"));
|
|
1164
|
+
}
|
|
1165
|
+
function printPackages(aggregated, mode) {
|
|
1166
|
+
const packages = aggregated.packageDistribution;
|
|
1146
1167
|
if (mode === "table") {
|
|
1147
|
-
|
|
1168
|
+
printPackagesTable(packages);
|
|
1148
1169
|
} else if (mode === "chart") {
|
|
1149
|
-
|
|
1170
|
+
printPackagesChart(packages);
|
|
1150
1171
|
}
|
|
1151
1172
|
}
|
|
1152
|
-
function
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
console.log(chalk__default.default.gray(" No patterns found"));
|
|
1173
|
+
function printPackagesTable(packages) {
|
|
1174
|
+
printHeader5();
|
|
1175
|
+
if (packages.length === 0) {
|
|
1176
|
+
console.log(chalk6__default.default.gray(" No packages found"));
|
|
1157
1177
|
return;
|
|
1158
1178
|
}
|
|
1159
1179
|
const table = new Table__default.default({
|
|
1160
|
-
head: ["
|
|
1180
|
+
head: ["Package", "Version", "Components", "Usage", "Percentage"],
|
|
1161
1181
|
style: {
|
|
1162
1182
|
head: ["cyan"],
|
|
1163
1183
|
border: ["gray"]
|
|
1164
1184
|
}
|
|
1165
1185
|
});
|
|
1166
|
-
|
|
1167
|
-
table.push([
|
|
1186
|
+
packages.forEach((pkg) => {
|
|
1187
|
+
table.push([
|
|
1188
|
+
pkg.packageName,
|
|
1189
|
+
pkg.version || "N/A",
|
|
1190
|
+
formatCount(pkg.componentCount),
|
|
1191
|
+
formatCount(pkg.usageCount),
|
|
1192
|
+
`${pkg.percentage.toFixed(1)}%`
|
|
1193
|
+
]);
|
|
1168
1194
|
});
|
|
1169
1195
|
console.log(table.toString());
|
|
1196
|
+
const totalComponents = packages.reduce(
|
|
1197
|
+
(sum, p) => sum + p.componentCount,
|
|
1198
|
+
0
|
|
1199
|
+
);
|
|
1200
|
+
const totalExternalUsage = packages.reduce((sum, p) => sum + p.usageCount, 0);
|
|
1201
|
+
console.log(
|
|
1202
|
+
chalk6__default.default.gray(
|
|
1203
|
+
`
|
|
1204
|
+
Total: ${formatCount(packages.length)} packages | ${formatCount(totalComponents)} unique components | ${formatCount(totalExternalUsage)} total usages`
|
|
1205
|
+
)
|
|
1206
|
+
);
|
|
1170
1207
|
}
|
|
1171
|
-
function
|
|
1172
|
-
|
|
1173
|
-
if (
|
|
1174
|
-
console.log(
|
|
1208
|
+
function printPackagesChart(packages) {
|
|
1209
|
+
printHeader5();
|
|
1210
|
+
if (packages.length === 0) {
|
|
1211
|
+
console.log(chalk6__default.default.gray(" No packages found"));
|
|
1175
1212
|
return;
|
|
1176
1213
|
}
|
|
1177
|
-
const
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1214
|
+
const maxBarWidth = 40;
|
|
1215
|
+
const maxPercentage = Math.max(...packages.map((p) => p.percentage));
|
|
1216
|
+
const maxLabelLength = Math.max(...packages.map((p) => p.packageName.length));
|
|
1217
|
+
packages.forEach((pkg) => {
|
|
1218
|
+
const barLength = Math.round(
|
|
1219
|
+
pkg.percentage / maxPercentage * maxBarWidth
|
|
1220
|
+
);
|
|
1221
|
+
const emptyLength = maxBarWidth - barLength;
|
|
1222
|
+
const paddedLabel = pkg.packageName.padEnd(maxLabelLength, " ");
|
|
1223
|
+
const bar = chalk6__default.default.green("\u2588".repeat(barLength)) + chalk6__default.default.gray("\u2591".repeat(emptyLength));
|
|
1224
|
+
console.log(
|
|
1225
|
+
`${paddedLabel} ${bar} ${chalk6__default.default.bold(pkg.percentage.toFixed(1) + "%")} (${pkg.usageCount})`
|
|
1226
|
+
);
|
|
1227
|
+
});
|
|
1228
|
+
}
|
|
1229
|
+
async function findFiles(pattern, ignorePatterns) {
|
|
1230
|
+
const files = await glob.glob(pattern, {
|
|
1231
|
+
ignore: ignorePatterns,
|
|
1232
|
+
nodir: true,
|
|
1233
|
+
absolute: true,
|
|
1234
|
+
windowsPathsNoEscape: true
|
|
1235
|
+
});
|
|
1236
|
+
return files;
|
|
1237
|
+
}
|
|
1238
|
+
var NpmLockfileAdapter = class {
|
|
1239
|
+
constructor() {
|
|
1240
|
+
this.name = "npm";
|
|
1241
|
+
this.supportedVersions = ["v2", "v3"];
|
|
1242
|
+
}
|
|
1243
|
+
detect(projectPath) {
|
|
1244
|
+
const lockfilePath = path__default.default.join(projectPath, "package-lock.json");
|
|
1245
|
+
return fs3__default.default.existsSync(lockfilePath) ? lockfilePath : null;
|
|
1246
|
+
}
|
|
1247
|
+
parse(lockFilePath) {
|
|
1248
|
+
try {
|
|
1249
|
+
const content = fs3__default.default.readFileSync(lockFilePath, "utf8");
|
|
1250
|
+
const lockData = JSON.parse(content);
|
|
1251
|
+
const versions = {};
|
|
1252
|
+
if (lockData.packages) {
|
|
1253
|
+
Object.entries(lockData.packages).forEach(
|
|
1254
|
+
([pkgPath, pkgData]) => {
|
|
1255
|
+
if (!pkgPath || pkgPath === "") return;
|
|
1256
|
+
const pkgName = pkgPath.replace(/^node_modules\//, "");
|
|
1257
|
+
if (pkgData.version) {
|
|
1258
|
+
versions[pkgName] = pkgData.version;
|
|
1259
|
+
}
|
|
1260
|
+
}
|
|
1261
|
+
);
|
|
1262
|
+
}
|
|
1263
|
+
if (lockData.dependencies && Object.keys(versions).length === 0) {
|
|
1264
|
+
let extractVersions = function(deps, prefix = "") {
|
|
1265
|
+
Object.entries(deps).forEach(([name, data]) => {
|
|
1266
|
+
const fullName = prefix ? `${prefix}/${name}` : name;
|
|
1267
|
+
if (data.version) {
|
|
1268
|
+
versions[fullName] = data.version;
|
|
1269
|
+
}
|
|
1270
|
+
if (data.dependencies) {
|
|
1271
|
+
extractVersions(data.dependencies, fullName);
|
|
1272
|
+
}
|
|
1273
|
+
});
|
|
1274
|
+
};
|
|
1275
|
+
extractVersions(lockData.dependencies);
|
|
1276
|
+
}
|
|
1277
|
+
return versions;
|
|
1278
|
+
} catch (error) {
|
|
1279
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1280
|
+
console.warn(`Warning: Could not parse package-lock.json: ${message}`);
|
|
1281
|
+
return {};
|
|
1282
|
+
}
|
|
1283
|
+
}
|
|
1284
|
+
};
|
|
1285
|
+
var PnpmLockfileAdapter = class {
|
|
1286
|
+
constructor() {
|
|
1287
|
+
this.name = "pnpm";
|
|
1288
|
+
this.supportedVersions = ["v5", "v6", "v9"];
|
|
1289
|
+
}
|
|
1290
|
+
detect(projectPath) {
|
|
1291
|
+
const lockfilePath = path__default.default.join(projectPath, "pnpm-lock.yaml");
|
|
1292
|
+
return fs3__default.default.existsSync(lockfilePath) ? lockfilePath : null;
|
|
1293
|
+
}
|
|
1294
|
+
parse(lockFilePath) {
|
|
1295
|
+
try {
|
|
1296
|
+
const content = fs3__default.default.readFileSync(lockFilePath, "utf8");
|
|
1297
|
+
const lockData = yaml__default.default.load(content);
|
|
1298
|
+
const versions = {};
|
|
1299
|
+
if (lockData.importers) {
|
|
1300
|
+
const rootImporter = lockData.importers["."];
|
|
1301
|
+
if (rootImporter) {
|
|
1302
|
+
if (rootImporter.dependencies) {
|
|
1303
|
+
for (const [name, data] of Object.entries(
|
|
1304
|
+
rootImporter.dependencies
|
|
1305
|
+
)) {
|
|
1306
|
+
if (typeof data === "object" && data !== null && "version" in data) {
|
|
1307
|
+
versions[name] = data.version;
|
|
1308
|
+
}
|
|
1309
|
+
}
|
|
1310
|
+
}
|
|
1311
|
+
if (rootImporter.devDependencies) {
|
|
1312
|
+
for (const [name, data] of Object.entries(
|
|
1313
|
+
rootImporter.devDependencies
|
|
1314
|
+
)) {
|
|
1315
|
+
if (typeof data === "object" && data !== null && "version" in data) {
|
|
1316
|
+
versions[name] = data.version;
|
|
1317
|
+
}
|
|
1318
|
+
}
|
|
1319
|
+
}
|
|
1320
|
+
}
|
|
1321
|
+
}
|
|
1322
|
+
if (lockData.packages && Object.keys(versions).length === 0) {
|
|
1323
|
+
Object.keys(lockData.packages).forEach((key) => {
|
|
1324
|
+
const match = key.match(/\/(.+?)\/(\d+\.\d+\.\d+.*?)(?:_|$)/);
|
|
1325
|
+
if (match) {
|
|
1326
|
+
const [, pkgName, version] = match;
|
|
1327
|
+
versions[pkgName] = version;
|
|
1328
|
+
}
|
|
1329
|
+
});
|
|
1330
|
+
}
|
|
1331
|
+
if (lockData.dependencies && Object.keys(versions).length === 0) {
|
|
1332
|
+
Object.entries(lockData.dependencies).forEach(
|
|
1333
|
+
([name, versionSpec]) => {
|
|
1334
|
+
if (typeof versionSpec === "string" && !versionSpec.startsWith("link:")) {
|
|
1335
|
+
versions[name] = versionSpec;
|
|
1336
|
+
} else if (typeof versionSpec === "object" && versionSpec.version) {
|
|
1337
|
+
versions[name] = versionSpec.version;
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1340
|
+
);
|
|
1341
|
+
}
|
|
1342
|
+
return versions;
|
|
1343
|
+
} catch (error) {
|
|
1344
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1345
|
+
console.warn(`Warning: Could not parse pnpm-lock.yaml: ${message}`);
|
|
1346
|
+
return {};
|
|
1347
|
+
}
|
|
1348
|
+
}
|
|
1349
|
+
};
|
|
1350
|
+
var YarnLockfileAdapter = class {
|
|
1351
|
+
constructor() {
|
|
1352
|
+
this.name = "yarn";
|
|
1353
|
+
this.supportedVersions = ["v1", "v2+"];
|
|
1354
|
+
}
|
|
1355
|
+
detect(projectPath) {
|
|
1356
|
+
const lockfilePath = path__default.default.join(projectPath, "yarn.lock");
|
|
1357
|
+
return fs3__default.default.existsSync(lockfilePath) ? lockfilePath : null;
|
|
1358
|
+
}
|
|
1359
|
+
parse(lockFilePath) {
|
|
1360
|
+
try {
|
|
1361
|
+
const content = fs3__default.default.readFileSync(lockFilePath, "utf8");
|
|
1362
|
+
const parsed = lockfile__default.default.parse(content);
|
|
1363
|
+
if (parsed.type !== "success") {
|
|
1364
|
+
console.warn("Warning: Failed to parse yarn.lock");
|
|
1365
|
+
return {};
|
|
1366
|
+
}
|
|
1367
|
+
const versions = {};
|
|
1368
|
+
Object.entries(parsed.object).forEach(([key, value]) => {
|
|
1369
|
+
let pkgName = key;
|
|
1370
|
+
if (key.startsWith("@")) {
|
|
1371
|
+
const match = key.match(/^(@[^@]+\/[^@]+)@/);
|
|
1372
|
+
if (match) {
|
|
1373
|
+
pkgName = match[1];
|
|
1374
|
+
}
|
|
1375
|
+
} else {
|
|
1376
|
+
const match = key.match(/^([^@]+)@/);
|
|
1377
|
+
if (match) {
|
|
1378
|
+
pkgName = match[1];
|
|
1379
|
+
}
|
|
1380
|
+
}
|
|
1381
|
+
if (value.version && (!versions[pkgName] || value.version)) {
|
|
1382
|
+
versions[pkgName] = value.version;
|
|
1383
|
+
}
|
|
1384
|
+
});
|
|
1385
|
+
return versions;
|
|
1386
|
+
} catch (error) {
|
|
1387
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1388
|
+
console.warn(`Warning: Could not parse yarn.lock: ${message}`);
|
|
1389
|
+
return {};
|
|
1390
|
+
}
|
|
1391
|
+
}
|
|
1392
|
+
};
|
|
1393
|
+
|
|
1394
|
+
// src/lock-parser/index.ts
|
|
1395
|
+
var LOCKFILE_ADAPTERS = [
|
|
1396
|
+
new NpmLockfileAdapter(),
|
|
1397
|
+
new YarnLockfileAdapter(),
|
|
1398
|
+
new PnpmLockfileAdapter()
|
|
1399
|
+
];
|
|
1400
|
+
function findAndParseLockfile(projectPath) {
|
|
1401
|
+
for (const adapter of LOCKFILE_ADAPTERS) {
|
|
1402
|
+
const lockfilePath = adapter.detect(projectPath);
|
|
1403
|
+
if (lockfilePath) {
|
|
1404
|
+
const versions = adapter.parse(lockfilePath);
|
|
1405
|
+
return {
|
|
1406
|
+
versions,
|
|
1407
|
+
lockfileType: adapter.name,
|
|
1408
|
+
lockfilePath,
|
|
1409
|
+
supportedVersions: adapter.supportedVersions
|
|
1410
|
+
};
|
|
1411
|
+
}
|
|
1412
|
+
}
|
|
1413
|
+
throw new Error("No supported lockfile found");
|
|
1182
1414
|
}
|
|
1183
1415
|
|
|
1184
1416
|
// src/commands/scan.ts
|
|
@@ -1187,51 +1419,70 @@ function registerScanCommand(program2) {
|
|
|
1187
1419
|
"[pattern]",
|
|
1188
1420
|
"Glob pattern for files to analyze (defaults to current directory recursively)",
|
|
1189
1421
|
"**/*.{tsx,jsx,ts,js}"
|
|
1422
|
+
).option("--ignore <pattern>", "Glob pattern for files to ignore", [
|
|
1423
|
+
"**/node_modules/**",
|
|
1424
|
+
"**/dist/**",
|
|
1425
|
+
"**/build/**"
|
|
1426
|
+
]).option(
|
|
1427
|
+
"--allow-packages <pattern>",
|
|
1428
|
+
"Glob pattern for what packages to scan",
|
|
1429
|
+
"ALL"
|
|
1430
|
+
// TO FIX
|
|
1190
1431
|
).option(
|
|
1191
|
-
"--
|
|
1192
|
-
"
|
|
1193
|
-
|
|
1194
|
-
).option("--summary [mode]", "Show summary stats (log, false)", "log").option("--details", "Show detailed pattern counts").option(
|
|
1195
|
-
"--
|
|
1196
|
-
"Show top components (log, table, chart)",
|
|
1197
|
-
"log"
|
|
1198
|
-
).option(
|
|
1199
|
-
"--components-usage [mode]",
|
|
1432
|
+
"--ignore-packages <pattern>",
|
|
1433
|
+
"Glob pattern for what packages to ignore",
|
|
1434
|
+
[]
|
|
1435
|
+
).option("--summary [mode]", "Show summary stats (log, false)", "log").option("--no-summary", "Do not show summary stats").option("--details", "Show detailed pattern counts").option(
|
|
1436
|
+
"--components [mode]",
|
|
1200
1437
|
"Show components table/chart (table, chart)",
|
|
1201
1438
|
"table"
|
|
1202
|
-
).option(
|
|
1439
|
+
).option("--no-components", "Do not show components").option(
|
|
1440
|
+
"--packages [mode]",
|
|
1441
|
+
"Show packages table/chart (table, chart)",
|
|
1442
|
+
"table"
|
|
1443
|
+
).option("--no-packages", "Do not show packages").option(
|
|
1203
1444
|
"--patterns [mode]",
|
|
1204
1445
|
"Show patterns table/chart (table, chart)",
|
|
1205
1446
|
"table"
|
|
1206
|
-
).action(async (pattern, options) => {
|
|
1447
|
+
).option("--no-patterns", "Do not show patterns").action(async (pattern, options) => {
|
|
1207
1448
|
const normalizedOptions = normalizeOptions(options);
|
|
1208
1449
|
await executeScan(pattern, normalizedOptions);
|
|
1209
1450
|
});
|
|
1210
1451
|
}
|
|
1452
|
+
function normalizeIgnorePatterns(ignore) {
|
|
1453
|
+
if (!ignore) {
|
|
1454
|
+
return [];
|
|
1455
|
+
}
|
|
1456
|
+
return Array.isArray(ignore) ? ignore : [ignore];
|
|
1457
|
+
}
|
|
1211
1458
|
function normalizeOptions(options) {
|
|
1212
1459
|
return {
|
|
1213
1460
|
verbose: options.verbose || false,
|
|
1214
1461
|
summary: options.summary === false || options.summary === "false" ? false : "log",
|
|
1215
1462
|
details: options.details || false,
|
|
1216
|
-
|
|
1217
|
-
|
|
1463
|
+
components: options.components || "table",
|
|
1464
|
+
packages: options.packages || "table",
|
|
1218
1465
|
patterns: options.patterns || "table",
|
|
1219
|
-
|
|
1466
|
+
ignore: normalizeIgnorePatterns(options.ignore)
|
|
1220
1467
|
};
|
|
1221
1468
|
}
|
|
1222
1469
|
async function executeScan(pattern, options) {
|
|
1223
1470
|
const startTime = Date.now();
|
|
1224
|
-
const spinner = ora__default.default("
|
|
1471
|
+
const spinner = ora__default.default("Parsing lockfile...").start();
|
|
1225
1472
|
try {
|
|
1226
|
-
const
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1473
|
+
const lockfileResult = findAndParseLockfile(process.cwd());
|
|
1474
|
+
spinner.succeed(
|
|
1475
|
+
chalk6__default.default.blue(
|
|
1476
|
+
`\u{1F4E6} Found ${lockfileResult.lockfileType} lockfile (supports: ${lockfileResult.supportedVersions.join(", ")}) - ${Object.keys(lockfileResult.versions).length} packages`
|
|
1477
|
+
)
|
|
1478
|
+
);
|
|
1479
|
+
spinner.start("Finding files...");
|
|
1480
|
+
const files = await findFiles(pattern, options.ignore);
|
|
1230
1481
|
if (files.length === 0) {
|
|
1231
|
-
spinner.fail(
|
|
1482
|
+
spinner.fail("TEST");
|
|
1232
1483
|
return;
|
|
1233
1484
|
}
|
|
1234
|
-
spinner.succeed(
|
|
1485
|
+
spinner.succeed(chalk6__default.default.green(` Found ${files.length} files`));
|
|
1235
1486
|
spinner.start("Analyzing files...");
|
|
1236
1487
|
const reports = [];
|
|
1237
1488
|
for (let i = 0; i < files.length; i++) {
|
|
@@ -1243,45 +1494,35 @@ async function executeScan(pattern, options) {
|
|
|
1243
1494
|
const report = parseFile(file);
|
|
1244
1495
|
if (report) {
|
|
1245
1496
|
reports.push(report);
|
|
1246
|
-
if (options.verbose) {
|
|
1247
|
-
spinner.stop();
|
|
1248
|
-
printVerbose(file, report);
|
|
1249
|
-
spinner.start();
|
|
1250
|
-
}
|
|
1251
1497
|
}
|
|
1252
1498
|
} catch (error) {
|
|
1253
1499
|
spinner.stop();
|
|
1254
|
-
console.error(
|
|
1500
|
+
console.error(chalk6__default.default.red(`Error analyzing ${file}: ${error.message}`));
|
|
1255
1501
|
spinner.start();
|
|
1256
1502
|
}
|
|
1257
1503
|
}
|
|
1258
1504
|
spinner.succeed(
|
|
1259
|
-
|
|
1505
|
+
chalk6__default.default.green(`Analysis complete! Analyzed ${reports.length} files`)
|
|
1260
1506
|
);
|
|
1261
1507
|
const elapsedTime = (Date.now() - startTime) / 1e3;
|
|
1262
|
-
const aggregated = aggregateReports(reports);
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
printSummary(aggregated, elapsedTime);
|
|
1508
|
+
const aggregated = aggregateReports(reports, lockfileResult.versions);
|
|
1509
|
+
if (options.packages) {
|
|
1510
|
+
printPackages(aggregated, options.packages);
|
|
1266
1511
|
}
|
|
1267
1512
|
if (options.details) {
|
|
1268
|
-
console.log("");
|
|
1269
1513
|
printDetails(aggregated);
|
|
1270
1514
|
}
|
|
1271
|
-
if (options.
|
|
1272
|
-
|
|
1273
|
-
printTopComponents(aggregated, options.topComponents);
|
|
1274
|
-
}
|
|
1275
|
-
if (options.componentsUsage) {
|
|
1276
|
-
console.log("");
|
|
1277
|
-
printComponentsUsage(aggregated, options.componentsUsage);
|
|
1515
|
+
if (options.components) {
|
|
1516
|
+
printComponents(aggregated, options.components);
|
|
1278
1517
|
}
|
|
1279
1518
|
if (options.patterns) {
|
|
1280
|
-
console.log("");
|
|
1281
1519
|
printPatterns(aggregated, options.patterns);
|
|
1282
1520
|
}
|
|
1521
|
+
if (options.summary) {
|
|
1522
|
+
printSummary(aggregated, elapsedTime);
|
|
1523
|
+
}
|
|
1283
1524
|
} catch (error) {
|
|
1284
|
-
spinner.fail(
|
|
1525
|
+
spinner.fail(chalk6__default.default.red("Analysis failed: " + error.message));
|
|
1285
1526
|
console.error(error);
|
|
1286
1527
|
process.exit(1);
|
|
1287
1528
|
}
|
|
@@ -1289,7 +1530,7 @@ async function executeScan(pattern, options) {
|
|
|
1289
1530
|
|
|
1290
1531
|
// package.json
|
|
1291
1532
|
var package_default = {
|
|
1292
|
-
version: "
|
|
1533
|
+
version: "1.0.0"};
|
|
1293
1534
|
|
|
1294
1535
|
// src/cli.ts
|
|
1295
1536
|
var program = new commander.Command();
|