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.
Files changed (4) hide show
  1. package/README.md +141 -145
  2. package/dist/cli.js +487 -246
  3. package/dist/cli.js.map +1 -1
  4. 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 chalk = require('chalk');
5
+ var chalk6 = require('chalk');
8
6
  var core = require('@swc/core');
9
- var fs = require('fs');
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 chalk__default = /*#__PURE__*/_interopDefault(chalk);
17
- var fs__default = /*#__PURE__*/_interopDefault(fs);
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 = fs__default.default.readFileSync(filePath, "utf8");
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(jsx.component, report);
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 findComponentSource(componentName, report) {
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) return namedImport.source;
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) return defaultImport.source;
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) return aliasedImport.source;
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 printVerbose(filePath, report) {
887
- const relativePath = path__default.default.relative(process.cwd(), filePath);
888
- console.log(chalk__default.default.gray(`[VERBOSE] Analyzing: ${relativePath}`));
889
- for (const jsx of report.patterns.usage.jsx) {
890
- console.log(chalk__default.default.gray(`[VERBOSE] Found JSX Usage: <${jsx.component}>`));
891
- }
892
- for (const imp of report.patterns.imports.named) {
893
- console.log(
894
- chalk__default.default.gray(`[VERBOSE] Found import: ${imp.name} from ${imp.source}`)
895
- );
896
- }
897
- for (const imp of report.patterns.imports.default) {
898
- console.log(
899
- chalk__default.default.gray(
900
- `[VERBOSE] Found default import: ${imp.name} from ${imp.source}`
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
- for (const cond of report.patterns.usage.conditional) {
928
- console.log(
929
- chalk__default.default.gray(`[VERBOSE] Found Conditional usage: ${cond.consequent}`)
930
- );
931
- }
932
- for (const arr of report.patterns.usage.arrays) {
933
- console.log(
934
- chalk__default.default.gray(
935
- `[VERBOSE] Found Array mapping with components: ${arr.components.join(", ")}`
936
- )
937
- );
938
- }
939
- for (const variable of report.patterns.usage.variables) {
940
- console.log(
941
- chalk__default.default.gray(
942
- `[VERBOSE] Found Variable assignment: ${variable.variable} = ${variable.assignment}`
943
- )
944
- );
945
- }
946
- for (const destructure of report.patterns.usage.destructuring) {
947
- console.log(
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
- if (report.patterns.advanced.portal.length > 0) {
975
- console.log(chalk__default.default.gray(`[VERBOSE] Found Portal usage`));
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
- console.log(chalk__default.default.gray(`[VERBOSE] ${"\u2500".repeat(80)}`));
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
- chalk__default.default.green(
982
- `[SUMMARY] Files analyzed: ${aggregated.filesAnalyzed.toLocaleString()}`
993
+ chalk6__default.default.green(
994
+ `Analysis completed successfully in ${formatDuration(elapsedTimeSeconds)}
995
+ `
983
996
  )
984
997
  );
985
- console.log(
986
- chalk__default.default.green(
987
- `[SUMMARY] Total imports: ${aggregated.totalImports.toLocaleString()}`
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
- console.log(
991
- chalk__default.default.green(
992
- `[SUMMARY] Total components: ${aggregated.totalComponents.toLocaleString()}`
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
- chalk__default.default.cyan(
999
- `[DETAILS] Total usage patterns: ${aggregated.totalUsagePatterns.toLocaleString()}`
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
- chalk__default.default.cyan(
1006
- `[DETAILS] ${pattern.displayName}: ${pattern.count.toLocaleString()}`
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(chalk__default.default.gray(" No data to display"));
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(chalk__default.default.gray(" All values are zero"));
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 = chalk__default.default.green(barChar.repeat(barLength)) + chalk__default.default.gray(emptyChar.repeat(emptyLength));
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-top-components.ts
1042
- function printTopComponents(aggregated, mode, topN = 10) {
1043
- const topComponents = aggregated.topComponents.slice(0, topN);
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 printTopComponentsLog(components) {
1053
- console.log(chalk__default.default.yellow.bold(`
1054
- \u{1F3C6} Top components`));
1055
- if (components.length === 0) {
1056
- console.log(chalk__default.default.gray(" No components found"));
1057
- return;
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 printTopComponentsTable(components) {
1071
- console.log(chalk__default.default.yellow(`[TOP-COMPONENTS] TABLE`));
1072
- if (components.length === 0) {
1073
- console.log(chalk__default.default.gray(" No components found"));
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: ["Rank", "Component", "Source", "Count"],
1092
+ head: ["Component", "Package", "Count"],
1078
1093
  style: {
1079
1094
  head: ["cyan"],
1080
1095
  border: ["gray"]
1081
1096
  }
1082
1097
  });
1083
- components.forEach((comp, idx) => {
1084
- const rank = idx + 1;
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 printTopComponentsChart(components) {
1091
- console.log(chalk__default.default.yellow(`[TOP-COMPONENTS] CHART`));
1092
- if (components.length === 0) {
1093
- console.log(chalk__default.default.gray(" No components found"));
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 = components.map((comp) => ({
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 printComponentsUsage(aggregated, mode) {
1103
- const components = aggregated.topComponents;
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
- printComponentsUsageTable(components);
1124
+ printPatternsTable(patterns);
1106
1125
  } else if (mode === "chart") {
1107
- printComponentsUsageChart(components);
1126
+ printPatternsChart(patterns);
1108
1127
  }
1109
1128
  }
1110
- function printComponentsUsageTable(components) {
1111
- console.log(chalk__default.default.magenta.bold(`
1112
- \u269B\uFE0F Components Usage
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: ["Component", "Source", "Version", "Count"],
1136
+ head: ["Pattern", "Count"],
1120
1137
  style: {
1121
1138
  head: ["cyan"],
1122
1139
  border: ["gray"]
1123
1140
  }
1124
1141
  });
1125
- components.forEach((comp) => {
1126
- table.push([comp.name, comp.source, "0.0.0", comp.count.toString()]);
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 printComponentsUsageChart(components) {
1131
- console.log(chalk__default.default.magenta.bold(`
1132
- \u269B\uFE0F Components Usage
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 = components.map((comp) => ({
1139
- label: comp.name,
1140
- value: comp.count
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 printPatterns(aggregated, mode) {
1145
- const patterns = aggregated.patternCounts.filter((p) => p.count > 0);
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
- printPatternsTable(patterns);
1168
+ printPackagesTable(packages);
1148
1169
  } else if (mode === "chart") {
1149
- printPatternsChart(patterns);
1170
+ printPackagesChart(packages);
1150
1171
  }
1151
1172
  }
1152
- function printPatternsTable(patterns) {
1153
- console.log(chalk__default.default.blue.bold(`
1154
- \u{1F50D} Code Patterns`));
1155
- if (patterns.length === 0) {
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: ["Pattern", "Count"],
1180
+ head: ["Package", "Version", "Components", "Usage", "Percentage"],
1161
1181
  style: {
1162
1182
  head: ["cyan"],
1163
1183
  border: ["gray"]
1164
1184
  }
1165
1185
  });
1166
- patterns.forEach((pattern) => {
1167
- table.push([pattern.displayName, pattern.count.toString()]);
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 printPatternsChart(patterns) {
1172
- console.log(chalk__default.default.blue(`[PATTERNS] CHART`));
1173
- if (patterns.length === 0) {
1174
- console.log(chalk__default.default.gray(" No patterns found"));
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 data = patterns.map((pattern) => ({
1178
- label: pattern.displayName,
1179
- value: pattern.count
1180
- }));
1181
- renderBarChart(data, { maxWidth: 50 });
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
- "--verbose",
1192
- "Show detailed file-by-file analysis with every pattern found",
1193
- false
1194
- ).option("--summary [mode]", "Show summary stats (log, false)", "log").option("--details", "Show detailed pattern counts").option(
1195
- "--top-components [mode]",
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
- topComponents: options.topComponents || "log",
1217
- componentsUsage: options.componentsUsage || "table",
1463
+ components: options.components || "table",
1464
+ packages: options.packages || "table",
1218
1465
  patterns: options.patterns || "table",
1219
- output: options.output
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("Finding files...").start();
1471
+ const spinner = ora__default.default("Parsing lockfile...").start();
1225
1472
  try {
1226
- const files = await glob.glob(pattern, {
1227
- ignore: ["node_modules/**", "dist/**", "build/**", ".git/**"],
1228
- absolute: true
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(chalk__default.default.red(`No files found matching pattern: ${pattern}`));
1482
+ spinner.fail("TEST");
1232
1483
  return;
1233
1484
  }
1234
- spinner.succeed(chalk__default.default.green(`Found ${files.length} files`));
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(chalk__default.default.red(`Error analyzing ${file}: ${error.message}`));
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
- chalk__default.default.green(`Analysis complete! Analyzed ${reports.length} files`)
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
- console.log("");
1264
- if (options.summary) {
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.topComponents) {
1272
- console.log("");
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(chalk__default.default.red("Analysis failed: " + error.message));
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: "0.0.1-alpha.0"};
1533
+ version: "1.0.0"};
1293
1534
 
1294
1535
  // src/cli.ts
1295
1536
  var program = new commander.Command();