@code-pushup/js-packages-plugin 0.29.0 → 0.34.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.
@@ -0,0 +1,10 @@
1
+ # Contributing to js-packages
2
+
3
+ ## Adding new package managers
4
+
5
+ In order to add a support for a new package manager, one needs to do the following.
6
+
7
+ 1. Expand `packageManagerSchema` in `config.ts`.
8
+ 2. Expand `<command>Args` in `runner/<command>/constants.ts` with a set of arguments to be run for a given package manager command.
9
+ 3. Create a custom type in `runner/<command>/types.ts` with relevant properties based on expected command JSON output.
10
+ 4. Create a function in `runner/<command>/unify-type.ts` that will transform JSON output into a normalized type `OutdatedResult` or `AuditResult` and add it to `normalized<command>Mapper` in `runner/<command>/constants.ts`.
package/README.md CHANGED
@@ -10,9 +10,14 @@ This plugin checks for known vulnerabilities and outdated dependencies.
10
10
  It supports the following package managers:
11
11
 
12
12
  - [NPM](https://docs.npmjs.com/)
13
- - [Yarn v1](https://classic.yarnpkg.com/docs/) & [Yarn v2+](https://yarnpkg.com/getting-started)
13
+ - [Yarn v1](https://classic.yarnpkg.com/docs/)
14
+ - [Yarn v2+](https://yarnpkg.com/getting-started)
15
+ - In order to check outdated dependencies for Yarn v2+, you need to install [`yarn-plugin-outdated`](https://github.com/mskelton/yarn-plugin-outdated).
14
16
  - [PNPM](https://pnpm.io/pnpm-cli)
15
17
 
18
+ > ![NOTE]
19
+ > As of now, Yarn v2 does not support security audit of optional dependencies. Only production and dev dependencies audits will be included in the report.
20
+
16
21
  ## Getting started
17
22
 
18
23
  1. If you haven't already, install [@code-pushup/cli](../cli/README.md) and create a configuration file.
@@ -97,7 +102,7 @@ The plugin accepts the following parameters:
97
102
 
98
103
  ### Audits and group
99
104
 
100
- This plugin provides a group per check for a convenient declaration in your config.
105
+ This plugin provides a group per check for a convenient declaration in your config. Each group contains audits for all supported groups of dependencies (`prod`, `dev` and `optional`).
101
106
 
102
107
  ```ts
103
108
  // ...
package/bin.js CHANGED
@@ -657,6 +657,20 @@ import chalk2 from "chalk";
657
657
  import { mkdir, readFile, readdir, rm, stat } from "node:fs/promises";
658
658
  import { join } from "node:path";
659
659
 
660
+ // packages/utils/src/lib/formatting.ts
661
+ function pluralize(text, amount) {
662
+ if (amount != null && Math.abs(amount) === 1) {
663
+ return text;
664
+ }
665
+ if (text.endsWith("y")) {
666
+ return `${text.slice(0, -1)}ies`;
667
+ }
668
+ if (text.endsWith("s")) {
669
+ return `${text}es`;
670
+ }
671
+ return `${text}s`;
672
+ }
673
+
660
674
  // packages/utils/src/lib/guards.ts
661
675
  function isPromiseFulfilledResult(result) {
662
676
  return result.status === "fulfilled";
@@ -739,7 +753,7 @@ var ProcessError = class extends Error {
739
753
  }
740
754
  };
741
755
  function executeProcess(cfg) {
742
- const { observer, cwd, command, args, alwaysResolve = false } = cfg;
756
+ const { observer, cwd, command, args, ignoreExitCode = false } = cfg;
743
757
  const { onStdout, onError, onComplete } = observer ?? {};
744
758
  const date = (/* @__PURE__ */ new Date()).toISOString();
745
759
  const start = performance.now();
@@ -759,7 +773,7 @@ function executeProcess(cfg) {
759
773
  });
760
774
  process2.on("close", (code) => {
761
775
  const timings = { date, duration: calcDuration(start) };
762
- if (code === 0 || alwaysResolve) {
776
+ if (code === 0 || ignoreExitCode) {
763
777
  onComplete?.();
764
778
  resolve({ code, stdout, stderr, ...timings });
765
779
  } else {
@@ -775,9 +789,25 @@ function executeProcess(cfg) {
775
789
  import { simpleGit } from "simple-git";
776
790
 
777
791
  // packages/utils/src/lib/transform.ts
792
+ import { platform } from "node:os";
778
793
  function objectToEntries(obj) {
779
794
  return Object.entries(obj);
780
795
  }
796
+ function objectFromEntries(entries) {
797
+ return Object.fromEntries(entries);
798
+ }
799
+ function toUnixNewlines(text) {
800
+ return platform() === "win32" ? text.replace(/\r\n/g, "\n") : text;
801
+ }
802
+ function fromJsonLines(jsonLines) {
803
+ const unifiedNewLines = toUnixNewlines(jsonLines).trim();
804
+ return JSON.parse(`[${unifiedNewLines.split("\n").join(",")}]`);
805
+ }
806
+ function apostrophize(text, upperCase) {
807
+ const lastCharMatch = text.match(/(\w)\W*$/);
808
+ const lastChar = lastCharMatch?.[1] ?? "";
809
+ return `${text}'${lastChar.toLocaleLowerCase() === "s" ? "" : upperCase ? "S" : "s"}`;
810
+ }
781
811
 
782
812
  // packages/utils/src/lib/progress.ts
783
813
  import chalk3 from "chalk";
@@ -797,6 +827,17 @@ var defaultAuditLevelMapping = {
797
827
  low: "warning",
798
828
  info: "info"
799
829
  };
830
+ var dependencyGroupToLong = {
831
+ prod: "dependencies",
832
+ dev: "devDependencies",
833
+ optional: "optionalDependencies"
834
+ };
835
+ var pkgManagerCommands = {
836
+ npm: "npm",
837
+ "yarn-classic": "yarn",
838
+ "yarn-modern": "yarn",
839
+ pnpm: "pnpm"
840
+ };
800
841
 
801
842
  // packages/plugin-js-packages/src/lib/config.ts
802
843
  var dependencyGroups = ["prod", "dev", "optional"];
@@ -834,6 +875,218 @@ var jsPackagesPluginConfigSchema = z15.object({
834
875
  }).default(defaultAuditLevelMapping).transform(fillAuditLevelMapping)
835
876
  });
836
877
 
878
+ // packages/plugin-js-packages/src/lib/runner/utils.ts
879
+ function filterAuditResult(result, key, referenceResult) {
880
+ if (result.vulnerabilities.length === 0) {
881
+ return result;
882
+ }
883
+ const uniqueResult = result.vulnerabilities.reduce(
884
+ (acc, ref) => {
885
+ const matchReference = referenceResult ?? acc;
886
+ const isMatch = matchReference.vulnerabilities.map((vulnerability) => vulnerability[key]).includes(ref[key]);
887
+ if (isMatch) {
888
+ return {
889
+ vulnerabilities: acc.vulnerabilities,
890
+ summary: {
891
+ ...acc.summary,
892
+ [ref.severity]: acc.summary[ref.severity] - 1,
893
+ total: acc.summary.total - 1
894
+ }
895
+ };
896
+ }
897
+ return {
898
+ vulnerabilities: [...acc.vulnerabilities, ref],
899
+ summary: acc.summary
900
+ };
901
+ },
902
+ { vulnerabilities: [], summary: result.summary }
903
+ );
904
+ return {
905
+ vulnerabilities: uniqueResult.vulnerabilities,
906
+ summary: uniqueResult.summary
907
+ };
908
+ }
909
+
910
+ // packages/plugin-js-packages/src/lib/runner/audit/unify-type.ts
911
+ function npmToAuditResult(output) {
912
+ const npmAudit = JSON.parse(output);
913
+ const vulnerabilities = objectToEntries(npmAudit.vulnerabilities).map(
914
+ ([name, detail]) => {
915
+ const advisory = npmToAdvisory(name, npmAudit.vulnerabilities);
916
+ return {
917
+ name: name.toString(),
918
+ severity: detail.severity,
919
+ versionRange: detail.range,
920
+ directDependency: detail.isDirect ? true : detail.effects[0] ?? "",
921
+ fixInformation: npmToFixInformation(detail.fixAvailable),
922
+ ...advisory != null && {
923
+ title: advisory.title,
924
+ url: advisory.url
925
+ }
926
+ };
927
+ }
928
+ );
929
+ return {
930
+ vulnerabilities,
931
+ summary: npmAudit.metadata.vulnerabilities
932
+ };
933
+ }
934
+ function npmToFixInformation(fixAvailable) {
935
+ if (typeof fixAvailable === "boolean") {
936
+ return fixAvailable ? "Fix is available." : "";
937
+ }
938
+ return `Fix available: Update \`${fixAvailable.name}\` to version **${fixAvailable.version}**${fixAvailable.isSemVerMajor ? " (breaking change)." : "."}`;
939
+ }
940
+ function npmToAdvisory(name, vulnerabilities, prevNodes = /* @__PURE__ */ new Set()) {
941
+ const advisory = vulnerabilities[name]?.via;
942
+ if (Array.isArray(advisory) && advisory.length > 0 && typeof advisory[0] === "object") {
943
+ return { title: advisory[0].title, url: advisory[0].url };
944
+ }
945
+ if (Array.isArray(advisory) && advisory.length > 0 && advisory.every((value) => typeof value === "string")) {
946
+ let advisoryInfo = null;
947
+ let newReferences = [];
948
+ let advisoryInfoFound = false;
949
+ for (const via of advisory) {
950
+ if (!prevNodes.has(via)) {
951
+ newReferences.push(via);
952
+ }
953
+ }
954
+ while (newReferences.length > 0 && !advisoryInfoFound) {
955
+ const ref = newReferences.pop();
956
+ prevNodes.add(ref);
957
+ const result = npmToAdvisory(ref, vulnerabilities, prevNodes);
958
+ if (result != null) {
959
+ advisoryInfo = { title: result.title, url: result.url };
960
+ advisoryInfoFound = true;
961
+ }
962
+ }
963
+ return advisoryInfo;
964
+ }
965
+ return null;
966
+ }
967
+ function yarnv1ToAuditResult(output) {
968
+ const yarnv1Result = fromJsonLines(output);
969
+ const [yarnv1Advisory, yarnv1Summary] = validateYarnv1Result(yarnv1Result);
970
+ const vulnerabilities = yarnv1Advisory.map(
971
+ ({ data: { resolution, advisory } }) => {
972
+ const { id, path } = resolution;
973
+ const directDependency = path.slice(0, path.indexOf(">"));
974
+ const {
975
+ module_name: name,
976
+ title,
977
+ url,
978
+ severity,
979
+ vulnerable_versions: versionRange,
980
+ recommendation: fixInformation
981
+ } = advisory;
982
+ return {
983
+ name,
984
+ title,
985
+ id,
986
+ url,
987
+ severity,
988
+ versionRange,
989
+ directDependency: name === directDependency ? true : directDependency,
990
+ fixInformation
991
+ };
992
+ }
993
+ );
994
+ const summary = {
995
+ ...yarnv1Summary.data.vulnerabilities,
996
+ total: Object.values(yarnv1Summary.data.vulnerabilities).reduce(
997
+ (acc, amount) => acc + amount,
998
+ 0
999
+ )
1000
+ };
1001
+ return filterAuditResult({ vulnerabilities, summary }, "id");
1002
+ }
1003
+ function validateYarnv1Result(result) {
1004
+ const summary = result.at(-1);
1005
+ if (summary?.type !== "auditSummary") {
1006
+ throw new Error("Invalid Yarn v1 audit result - no summary found.");
1007
+ }
1008
+ const vulnerabilities = result.filter(
1009
+ (item) => item.type === "auditAdvisory"
1010
+ );
1011
+ return [vulnerabilities, summary];
1012
+ }
1013
+ function yarnv2ToAuditResult(output) {
1014
+ const yarnv2Audit = JSON.parse(output);
1015
+ const vulnerabilities = Object.values(yarnv2Audit.advisories).map(
1016
+ ({
1017
+ module_name: name,
1018
+ severity,
1019
+ title,
1020
+ url,
1021
+ vulnerable_versions: versionRange,
1022
+ recommendation: fixInformation,
1023
+ findings
1024
+ }) => {
1025
+ const directDep = findings[0]?.paths[0];
1026
+ return {
1027
+ name,
1028
+ severity,
1029
+ title,
1030
+ url,
1031
+ versionRange,
1032
+ fixInformation,
1033
+ directDependency: directDep != null && directDep !== name ? directDep : true
1034
+ };
1035
+ }
1036
+ );
1037
+ return {
1038
+ vulnerabilities,
1039
+ summary: {
1040
+ ...yarnv2Audit.metadata.vulnerabilities,
1041
+ total: getVulnerabilitiesTotal(yarnv2Audit.metadata.vulnerabilities)
1042
+ }
1043
+ };
1044
+ }
1045
+ function pnpmToAuditResult(output) {
1046
+ const pnpmResult = JSON.parse(output);
1047
+ const vulnerabilities = Object.values(pnpmResult.advisories).map(
1048
+ ({
1049
+ module_name: name,
1050
+ id,
1051
+ title,
1052
+ url,
1053
+ severity,
1054
+ vulnerable_versions: versionRange,
1055
+ recommendation: fixInformation,
1056
+ findings
1057
+ }) => {
1058
+ const path = findings[0]?.paths[0];
1059
+ return {
1060
+ name,
1061
+ id,
1062
+ title,
1063
+ url,
1064
+ severity,
1065
+ versionRange,
1066
+ directDependency: path == null ? true : pnpmToDirectDependency(path),
1067
+ fixInformation
1068
+ };
1069
+ }
1070
+ );
1071
+ return {
1072
+ vulnerabilities,
1073
+ summary: {
1074
+ ...pnpmResult.metadata.vulnerabilities,
1075
+ total: getVulnerabilitiesTotal(pnpmResult.metadata.vulnerabilities)
1076
+ }
1077
+ };
1078
+ }
1079
+ function pnpmToDirectDependency(path) {
1080
+ const deps = path.split(" > ").slice(1);
1081
+ if (deps.length <= 1) {
1082
+ return true;
1083
+ }
1084
+ return deps[0]?.split("@")[0] ?? true;
1085
+ }
1086
+ function getVulnerabilitiesTotal(summary) {
1087
+ return Object.values(summary).reduce((acc, value) => acc + value, 0);
1088
+ }
1089
+
837
1090
  // packages/plugin-js-packages/src/lib/runner/audit/constants.ts
838
1091
  var auditScoreModifiers = {
839
1092
  critical: 1,
@@ -842,20 +1095,65 @@ var auditScoreModifiers = {
842
1095
  low: 0.02,
843
1096
  info: 0.01
844
1097
  };
1098
+ var normalizeAuditMapper = {
1099
+ npm: npmToAuditResult,
1100
+ "yarn-classic": yarnv1ToAuditResult,
1101
+ "yarn-modern": yarnv2ToAuditResult,
1102
+ pnpm: pnpmToAuditResult
1103
+ };
1104
+ var filterNpmAuditResults = (results) => ({
1105
+ prod: results.prod,
1106
+ dev: filterAuditResult(results.dev, "name", results.prod),
1107
+ optional: filterAuditResult(results.optional, "name", results.prod)
1108
+ });
1109
+ var filterPnpmAuditResults = (results) => ({
1110
+ prod: results.prod,
1111
+ dev: results.dev,
1112
+ optional: filterAuditResult(
1113
+ filterAuditResult(results.optional, "id", results.prod),
1114
+ "id",
1115
+ results.dev
1116
+ )
1117
+ });
1118
+ var postProcessingAuditMapper = {
1119
+ npm: filterNpmAuditResults,
1120
+ // prod dependencies need to be filtered out manually since v10
1121
+ pnpm: filterPnpmAuditResults
1122
+ // optional dependencies don't have an exclusive option so they need duplicates filtered out
1123
+ };
1124
+ var npmDependencyOptions = {
1125
+ prod: ["--omit=dev", "--omit=optional"],
1126
+ dev: ["--include=dev", "--omit=optional"],
1127
+ optional: ["--include=optional", "--omit=dev"]
1128
+ };
1129
+ var yarnv2EnvironmentOptions = {
1130
+ prod: "production",
1131
+ dev: "development",
1132
+ optional: ""
1133
+ };
1134
+ var pnpmDependencyOptions = {
1135
+ prod: ["--prod", "--no-optional"],
1136
+ dev: ["--dev", "--no-optional"],
1137
+ optional: []
1138
+ };
1139
+ var auditArgs = (groupDep) => ({
1140
+ npm: [...npmDependencyOptions[groupDep], "--audit-level=none"],
1141
+ "yarn-classic": ["--groups", dependencyGroupToLong[groupDep]],
1142
+ "yarn-modern": ["--environment", yarnv2EnvironmentOptions[groupDep]],
1143
+ pnpm: [...pnpmDependencyOptions[groupDep]]
1144
+ });
845
1145
 
846
1146
  // packages/plugin-js-packages/src/lib/runner/audit/transform.ts
847
- function auditResultToAuditOutput(result, dependenciesType, auditLevelMapping) {
1147
+ function auditResultToAuditOutput(result, packageManager, dependenciesType, auditLevelMapping) {
848
1148
  const issues = vulnerabilitiesToIssues(
849
1149
  result.vulnerabilities,
850
1150
  auditLevelMapping
851
1151
  );
852
1152
  return {
853
- slug: `npm-audit-${dependenciesType}`,
854
- score: calculateAuditScore(result.metadata.vulnerabilities),
855
- value: result.metadata.vulnerabilities.total,
856
- displayValue: vulnerabilitiesToDisplayValue(
857
- result.metadata.vulnerabilities
858
- ),
1153
+ slug: `${packageManager}-audit-${dependenciesType}`,
1154
+ score: calculateAuditScore(result.summary),
1155
+ value: result.summary.total,
1156
+ displayValue: summaryToDisplayValue(result.summary),
859
1157
  ...issues.length > 0 && { details: { issues } }
860
1158
  };
861
1159
  }
@@ -874,31 +1172,26 @@ function calculateAuditScore(stats) {
874
1172
  1
875
1173
  );
876
1174
  }
877
- function vulnerabilitiesToDisplayValue(vulnerabilities) {
878
- if (vulnerabilities.total === 0) {
1175
+ function summaryToDisplayValue(summary) {
1176
+ if (summary.total === 0) {
879
1177
  return "0 vulnerabilities";
880
1178
  }
881
- const vulnerabilityStats = packageAuditLevels.map(
882
- (level) => vulnerabilities[level] > 0 ? `${vulnerabilities[level]} ${level}` : ""
883
- ).filter((text) => text !== "").join(", ");
884
- return `${vulnerabilities.total} ${vulnerabilities.total === 1 ? "vulnerability" : "vulnerabilities"} (${vulnerabilityStats})`;
1179
+ const vulnerabilityStats = packageAuditLevels.map((level) => summary[level] > 0 ? `${summary[level]} ${level}` : "").filter((text) => text !== "").join(", ");
1180
+ return `${summary.total} ${summary.total === 1 ? "vulnerability" : "vulnerabilities"} (${vulnerabilityStats})`;
885
1181
  }
886
1182
  function vulnerabilitiesToIssues(vulnerabilities, auditLevelMapping) {
887
- if (Object.keys(vulnerabilities).length === 0) {
1183
+ if (vulnerabilities.length === 0) {
888
1184
  return [];
889
1185
  }
890
- return Object.values(vulnerabilities).map((detail) => {
891
- const versionRange = detail.range === "*" ? "**all** versions" : `versions **${detail.range}**`;
892
- const vulnerabilitySummary = `\`${detail.name}\` dependency has a **${detail.severity}** vulnerability in ${versionRange}.`;
893
- const fixInformation = typeof detail.fixAvailable === "boolean" ? `Fix is ${detail.fixAvailable ? "" : "not "}available.` : `Fix available: Update \`${detail.fixAvailable.name}\` to version **${detail.fixAvailable.version}**${detail.fixAvailable.isSemVerMajor ? " (breaking change)." : "."}`;
894
- if (Array.isArray(detail.via) && detail.via.length > 0 && typeof detail.via[0] === "object") {
895
- return {
896
- message: `${vulnerabilitySummary} ${fixInformation} More information: [${detail.via[0].title}](${detail.via[0].url})`,
897
- severity: auditLevelMapping[detail.severity]
898
- };
899
- }
1186
+ return vulnerabilities.map((detail) => {
1187
+ const versionRange = detail.versionRange === "*" ? "**all** versions" : `versions **${detail.versionRange}**`;
1188
+ const directDependency = typeof detail.directDependency === "string" ? `\`${detail.directDependency}\`` : "";
1189
+ const depHierarchy = directDependency === "" ? `\`${detail.name}\` dependency` : `${apostrophize(directDependency)} dependency \`${detail.name}\``;
1190
+ const vulnerabilitySummary = `has a **${detail.severity}** vulnerability in ${versionRange}.`;
1191
+ const fixInfo = detail.fixInformation ? ` ${detail.fixInformation}` : "";
1192
+ const additionalInfo = detail.title != null && detail.url != null ? ` More information: [${detail.title}](${detail.url})` : "";
900
1193
  return {
901
- message: `${vulnerabilitySummary} ${fixInformation}`,
1194
+ message: `${depHierarchy} ${vulnerabilitySummary}${fixInfo}${additionalInfo}`,
902
1195
  severity: auditLevelMapping[detail.severity]
903
1196
  };
904
1197
  });
@@ -914,67 +1207,140 @@ var PLUGIN_CONFIG_PATH = join2(
914
1207
  "plugin-config.json"
915
1208
  );
916
1209
 
1210
+ // packages/plugin-js-packages/src/lib/runner/outdated/unify-type.ts
1211
+ function npmToOutdatedResult(output) {
1212
+ const npmOutdated = JSON.parse(output);
1213
+ return objectToEntries(npmOutdated).filter(
1214
+ (entry) => entry[1].current != null
1215
+ ).map(([name, overview]) => ({
1216
+ name,
1217
+ current: overview.current,
1218
+ latest: overview.latest,
1219
+ type: overview.type,
1220
+ ...overview.homepage != null && { url: overview.homepage }
1221
+ }));
1222
+ }
1223
+ function yarnv1ToOutdatedResult(output) {
1224
+ const yarnv1Outdated = fromJsonLines(output);
1225
+ const dependencies = yarnv1Outdated[1].data.body;
1226
+ return dependencies.map(([name, current, _, latest, __, type, url]) => ({
1227
+ name,
1228
+ current,
1229
+ latest,
1230
+ type,
1231
+ url
1232
+ }));
1233
+ }
1234
+ function yarnv2ToOutdatedResult(output) {
1235
+ const npmOutdated = JSON.parse(output);
1236
+ return npmOutdated.map(({ name, current, latest, type }) => ({
1237
+ name,
1238
+ current,
1239
+ latest,
1240
+ type
1241
+ }));
1242
+ }
1243
+ function pnpmToOutdatedResult(output) {
1244
+ const pnpmOutdated = JSON.parse(output);
1245
+ return objectToEntries(pnpmOutdated).map(
1246
+ ([name, { current, latest, dependencyType: type }]) => ({
1247
+ name,
1248
+ current,
1249
+ latest,
1250
+ type
1251
+ })
1252
+ );
1253
+ }
1254
+
917
1255
  // packages/plugin-js-packages/src/lib/runner/outdated/constants.ts
918
1256
  var outdatedSeverity = {
919
1257
  major: "error",
920
1258
  minor: "warning",
921
1259
  patch: "info"
922
1260
  };
1261
+ var normalizeOutdatedMapper = {
1262
+ npm: npmToOutdatedResult,
1263
+ "yarn-classic": yarnv1ToOutdatedResult,
1264
+ "yarn-modern": yarnv2ToOutdatedResult,
1265
+ pnpm: pnpmToOutdatedResult
1266
+ };
1267
+ var outdatedArgs = {
1268
+ npm: ["--long"],
1269
+ "yarn-classic": [],
1270
+ "yarn-modern": [],
1271
+ pnpm: []
1272
+ };
1273
+
1274
+ // packages/plugin-js-packages/src/lib/runner/outdated/types.ts
1275
+ var versionType = ["major", "minor", "patch"];
923
1276
 
924
1277
  // packages/plugin-js-packages/src/lib/runner/outdated/transform.ts
925
- function outdatedResultToAuditOutput(result, dependenciesType) {
926
- const validDependencies = objectToEntries(result).filter(
927
- (entry) => entry[1].current != null
928
- ).filter(
929
- ([, detail]) => dependenciesType === "prod" ? detail.type === "dependencies" : detail.type === `${dependenciesType}Dependencies`
1278
+ function outdatedResultToAuditOutput(result, packageManager, dependencyGroup) {
1279
+ const relevantDependencies = result.filter(
1280
+ (dep) => dep.type === dependencyGroupToLong[dependencyGroup]
1281
+ );
1282
+ const outdatedDependencies = relevantDependencies.filter(
1283
+ (dep) => dep.current !== dep.latest
930
1284
  );
931
- const outdatedDependencies = validDependencies.filter(
932
- ([, versions]) => versions.current !== versions.wanted
1285
+ const outdatedStats = outdatedDependencies.reduce(
1286
+ (acc, dep) => {
1287
+ const outdatedLevel = getOutdatedLevel(dep.current, dep.latest);
1288
+ return { ...acc, [outdatedLevel]: acc[outdatedLevel] + 1 };
1289
+ },
1290
+ { major: 0, minor: 0, patch: 0 }
933
1291
  );
934
- const majorOutdatedAmount = outdatedDependencies.filter(
935
- ([, versions]) => getOutdatedLevel(versions.current, versions.wanted) === "major"
936
- ).length;
937
1292
  const issues = outdatedDependencies.length === 0 ? [] : outdatedToIssues(outdatedDependencies);
938
1293
  return {
939
- slug: `npm-outdated-${dependenciesType}`,
1294
+ slug: `${packageManager}-outdated-${dependencyGroup}`,
940
1295
  score: calculateOutdatedScore(
941
- majorOutdatedAmount,
942
- validDependencies.length
1296
+ outdatedStats.major,
1297
+ relevantDependencies.length
943
1298
  ),
944
1299
  value: outdatedDependencies.length,
945
- displayValue: outdatedToDisplayValue(
946
- majorOutdatedAmount,
947
- outdatedDependencies.length
948
- ),
1300
+ displayValue: outdatedToDisplayValue(outdatedStats),
949
1301
  ...issues.length > 0 && { details: { issues } }
950
1302
  };
951
1303
  }
952
1304
  function calculateOutdatedScore(majorOutdated, totalDeps) {
953
1305
  return totalDeps > 0 ? (totalDeps - majorOutdated) / totalDeps : 1;
954
1306
  }
955
- function outdatedToDisplayValue(majorOutdated, totalOutdated) {
956
- return totalOutdated === 0 ? "all dependencies are up to date" : majorOutdated > 0 ? `${majorOutdated} out of ${totalOutdated} outdated dependencies require major update` : `${totalOutdated} outdated ${totalOutdated === 1 ? "dependency" : "dependencies"}`;
1307
+ function outdatedToDisplayValue(stats) {
1308
+ const total = stats.major + stats.minor + stats.patch;
1309
+ const versionBreakdown = versionType.map((version) => stats[version] > 0 ? `${stats[version]} ${version}` : "").filter((text) => text !== "");
1310
+ if (versionBreakdown.length === 0) {
1311
+ return "all dependencies are up to date";
1312
+ }
1313
+ if (versionBreakdown.length > 1) {
1314
+ return `${total} outdated package versions (${versionBreakdown.join(
1315
+ ", "
1316
+ )})`;
1317
+ }
1318
+ return `${versionBreakdown[0]} outdated package ${pluralize(
1319
+ "version",
1320
+ total
1321
+ )}`;
957
1322
  }
958
1323
  function outdatedToIssues(dependencies) {
959
- return dependencies.map(([name, versions]) => {
960
- const outdatedLevel = getOutdatedLevel(versions.current, versions.wanted);
961
- const packageReference = versions.homepage == null ? `\`${name}\`` : `[\`${name}\`](${versions.homepage})`;
1324
+ return dependencies.map((dep) => {
1325
+ const { name, current, latest, url } = dep;
1326
+ const outdatedLevel = getOutdatedLevel(current, latest);
1327
+ const packageReference = url == null ? `\`${name}\`` : `[\`${name}\`](${url})`;
962
1328
  return {
963
- message: `Package ${packageReference} requires a **${outdatedLevel}** update from **${versions.current}** to **${versions.wanted}**.`,
1329
+ message: `Package ${packageReference} requires a **${outdatedLevel}** update from **${current}** to **${latest}**.`,
964
1330
  severity: outdatedSeverity[outdatedLevel]
965
1331
  };
966
1332
  });
967
1333
  }
968
- function getOutdatedLevel(currentFullVersion, wantedFullVersion) {
1334
+ function getOutdatedLevel(currentFullVersion, latestFullVersion) {
969
1335
  const current = splitPackageVersion(currentFullVersion);
970
- const wanted = splitPackageVersion(wantedFullVersion);
971
- if (current.major < wanted.major) {
1336
+ const latest = splitPackageVersion(latestFullVersion);
1337
+ if (current.major < latest.major) {
972
1338
  return "major";
973
1339
  }
974
- if (current.minor < wanted.minor) {
1340
+ if (current.minor < latest.minor) {
975
1341
  return "minor";
976
1342
  }
977
- if (current.patch < wanted.patch) {
1343
+ if (current.patch < latest.patch) {
978
1344
  return "patch";
979
1345
  }
980
1346
  throw new Error("Package is not outdated.");
@@ -998,42 +1364,62 @@ async function executeRunner() {
998
1364
  }
999
1365
  async function processOutdated(packageManager) {
1000
1366
  const { stdout } = await executeProcess({
1001
- command: packageManager,
1002
- args: ["outdated", "--json", "--long"],
1003
- alwaysResolve: true
1004
- // npm outdated returns exit code 1 when outdated dependencies are found
1367
+ command: pkgManagerCommands[packageManager],
1368
+ args: ["outdated", "--json", ...outdatedArgs[packageManager]],
1369
+ cwd: process.cwd(),
1370
+ ignoreExitCode: true
1371
+ // outdated returns exit code 1 when outdated dependencies are found
1005
1372
  });
1006
- const outdatedResult = JSON.parse(stdout);
1373
+ const normalizedResult = normalizeOutdatedMapper[packageManager](stdout);
1007
1374
  return dependencyGroups.map(
1008
- (dep) => outdatedResultToAuditOutput(outdatedResult, dep)
1375
+ (dep) => outdatedResultToAuditOutput(normalizedResult, packageManager, dep)
1009
1376
  );
1010
1377
  }
1011
1378
  async function processAudit(packageManager, auditLevelMapping) {
1379
+ const supportedDepGroups = packageManager === "yarn-modern" ? dependencyGroups.filter((dep) => dep !== "optional") : dependencyGroups;
1012
1380
  const auditResults = await Promise.allSettled(
1013
- dependencyGroups.map(async (dep) => {
1014
- const { stdout } = await executeProcess({
1015
- command: packageManager,
1016
- args: ["audit", ...getNpmAuditOptions(dep)]
1017
- });
1018
- const auditResult = JSON.parse(stdout);
1019
- return auditResultToAuditOutput(auditResult, dep, auditLevelMapping);
1020
- })
1381
+ supportedDepGroups.map(
1382
+ async (dep) => {
1383
+ const { stdout } = await executeProcess({
1384
+ command: pkgManagerCommands[packageManager],
1385
+ args: getAuditCommandArgs(packageManager, dep),
1386
+ cwd: process.cwd(),
1387
+ ignoreExitCode: packageManager === "yarn-classic" || packageManager === "pnpm"
1388
+ // yarn v1 and PNPM do not have exit code configuration
1389
+ });
1390
+ return [dep, normalizeAuditMapper[packageManager](stdout)];
1391
+ }
1392
+ )
1021
1393
  );
1022
1394
  const rejected = auditResults.filter(isPromiseRejectedResult);
1023
1395
  if (rejected.length > 0) {
1024
1396
  rejected.map((result) => {
1025
1397
  console.error(result.reason);
1026
1398
  });
1027
- throw new Error(`JS Packages plugin: Running npm audit failed.`);
1399
+ throw new Error(
1400
+ `JS Packages plugin: Running ${pkgManagerCommands[packageManager]} audit failed.`
1401
+ );
1028
1402
  }
1029
- return auditResults.filter(isPromiseFulfilledResult).map((x) => x.value);
1403
+ const fulfilled = objectFromEntries(
1404
+ auditResults.filter(isPromiseFulfilledResult).map((x) => x.value)
1405
+ );
1406
+ const uniqueResults = postProcessingAuditMapper[packageManager]?.(fulfilled) ?? fulfilled;
1407
+ return supportedDepGroups.map(
1408
+ (group) => auditResultToAuditOutput(
1409
+ uniqueResults[group],
1410
+ packageManager,
1411
+ group,
1412
+ auditLevelMapping
1413
+ )
1414
+ );
1030
1415
  }
1031
- function getNpmAuditOptions(currentDep) {
1032
- const flags = [
1033
- `--include=${currentDep}`,
1034
- ...dependencyGroups.filter((dep) => dep !== currentDep).map((dep) => `--omit=${dep}`)
1416
+ function getAuditCommandArgs(packageManager, group) {
1417
+ return [
1418
+ ...packageManager === "yarn-modern" ? ["npm"] : [],
1419
+ "audit",
1420
+ "--json",
1421
+ ...auditArgs(group)[packageManager]
1035
1422
  ];
1036
- return [...flags, "--json", "--audit-level=none"];
1037
1423
  }
1038
1424
 
1039
1425
  // packages/plugin-js-packages/src/bin.ts
package/index.js CHANGED
@@ -4,7 +4,7 @@ import { fileURLToPath } from "node:url";
4
4
 
5
5
  // packages/plugin-js-packages/package.json
6
6
  var name = "@code-pushup/js-packages-plugin";
7
- var version = "0.29.0";
7
+ var version = "0.34.0";
8
8
 
9
9
  // packages/plugin-js-packages/src/lib/config.ts
10
10
  import { z as z15 } from "zod";
@@ -834,9 +834,9 @@ async function jsPackagesPlugin(config) {
834
834
  );
835
835
  return {
836
836
  slug: "js-packages",
837
- title: "Plugin for JS packages",
837
+ title: "JS Packages",
838
838
  icon: pkgManagerIcons[pkgManager],
839
- description: "This plugin runs audit to uncover vulnerabilities and lists outdated dependencies. It supports npm, yarn classic and berry, pnpm package managers.",
839
+ description: "This plugin runs audit to uncover vulnerabilities and lists outdated dependencies. It supports npm, yarn classic, yarn modern, and pnpm package managers.",
840
840
  docsUrl: pkgManagerDocs[pkgManager],
841
841
  packageName: name,
842
842
  version,
@@ -854,9 +854,15 @@ function createGroups(pkgManager, checks) {
854
854
  docsUrl: auditDocs[pkgManager],
855
855
  refs: [
856
856
  // eslint-disable-next-line no-magic-numbers
857
- { slug: `${pkgManager}-audit-prod`, weight: 8 },
857
+ { slug: `${pkgManager}-audit-prod`, weight: 3 },
858
858
  { slug: `${pkgManager}-audit-dev`, weight: 1 },
859
- { slug: `${pkgManager}-audit-optional`, weight: 1 }
859
+ // Yarn v2 does not support audit for optional dependencies
860
+ ...pkgManager === "yarn-modern" ? [] : [
861
+ {
862
+ slug: `${pkgManager}-audit-optional`,
863
+ weight: 1
864
+ }
865
+ ]
860
866
  ]
861
867
  },
862
868
  outdated: {
@@ -866,7 +872,7 @@ function createGroups(pkgManager, checks) {
866
872
  docsUrl: outdatedDocs[pkgManager],
867
873
  refs: [
868
874
  // eslint-disable-next-line no-magic-numbers
869
- { slug: `${pkgManager}-outdated-prod`, weight: 8 },
875
+ { slug: `${pkgManager}-outdated-prod`, weight: 3 },
870
876
  { slug: `${pkgManager}-outdated-dev`, weight: 1 },
871
877
  { slug: `${pkgManager}-outdated-optional`, weight: 1 }
872
878
  ]
@@ -888,12 +894,15 @@ function createAudits(pkgManager, checks) {
888
894
  description: getAuditDescription(check, "dev"),
889
895
  docsUrl: dependencyDocs.dev
890
896
  },
891
- {
892
- slug: `${pkgManager}-${check}-optional`,
893
- title: getAuditTitle(pkgManager, check, "optional"),
894
- description: getAuditDescription(check, "optional"),
895
- docsUrl: dependencyDocs.optional
896
- }
897
+ // Yarn v2 does not support audit for optional dependencies
898
+ ...pkgManager === "yarn-modern" && check === "audit" ? [] : [
899
+ {
900
+ slug: `${pkgManager}-${check}-optional`,
901
+ title: getAuditTitle(pkgManager, check, "optional"),
902
+ description: getAuditDescription(check, "optional"),
903
+ docsUrl: dependencyDocs.optional
904
+ }
905
+ ]
897
906
  ]);
898
907
  }
899
908
  function getAuditTitle(pkgManager, check, dependencyType) {
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "@code-pushup/js-packages-plugin",
3
- "version": "0.29.0",
3
+ "version": "0.34.0",
4
4
  "dependencies": {
5
- "@code-pushup/models": "*",
6
- "@code-pushup/utils": "*",
5
+ "@code-pushup/models": "0.34.0",
6
+ "@code-pushup/utils": "0.34.0",
7
7
  "zod": "^3.22.4"
8
8
  },
9
9
  "license": "MIT",
@@ -9,15 +9,16 @@ export type PackageManager = z.infer<typeof packageManagerSchema>;
9
9
  export declare const packageAuditLevels: readonly ["critical", "high", "moderate", "low", "info"];
10
10
  declare const packageAuditLevelSchema: z.ZodEnum<["critical", "high", "moderate", "low", "info"]>;
11
11
  export type PackageAuditLevel = z.infer<typeof packageAuditLevelSchema>;
12
- export declare function fillAuditLevelMapping(mapping: Partial<Record<PackageAuditLevel, IssueSeverity>>): Record<PackageAuditLevel, IssueSeverity>;
12
+ export type AuditSeverity = Record<PackageAuditLevel, IssueSeverity>;
13
+ export declare function fillAuditLevelMapping(mapping: Partial<AuditSeverity>): AuditSeverity;
13
14
  export declare const jsPackagesPluginConfigSchema: z.ZodObject<{
14
15
  checks: z.ZodDefault<z.ZodArray<z.ZodEnum<["audit", "outdated"]>, "many">>;
15
16
  packageManager: z.ZodEnum<["npm", "yarn-classic", "yarn-modern", "pnpm"]>;
16
- auditLevelMapping: z.ZodEffects<z.ZodDefault<z.ZodRecord<z.ZodEnum<["critical", "high", "moderate", "low", "info"]>, z.ZodEnum<["info", "warning", "error"]>>>, Record<"info" | "critical" | "high" | "moderate" | "low", "error" | "info" | "warning">, Partial<Record<"info" | "critical" | "high" | "moderate" | "low", "error" | "info" | "warning">> | undefined>;
17
+ auditLevelMapping: z.ZodEffects<z.ZodDefault<z.ZodRecord<z.ZodEnum<["critical", "high", "moderate", "low", "info"]>, z.ZodEnum<["info", "warning", "error"]>>>, AuditSeverity, Partial<Record<"info" | "critical" | "high" | "moderate" | "low", "error" | "info" | "warning">> | undefined>;
17
18
  }, "strip", z.ZodTypeAny, {
18
19
  checks: ("audit" | "outdated")[];
19
20
  packageManager: "npm" | "pnpm" | "yarn-classic" | "yarn-modern";
20
- auditLevelMapping: Record<"info" | "critical" | "high" | "moderate" | "low", "error" | "info" | "warning">;
21
+ auditLevelMapping: AuditSeverity;
21
22
  }, {
22
23
  packageManager: "npm" | "pnpm" | "yarn-classic" | "yarn-modern";
23
24
  checks?: ("audit" | "outdated")[] | undefined;
@@ -1,6 +1,9 @@
1
1
  import { IssueSeverity, MaterialIcon } from '@code-pushup/models';
2
2
  import type { DependencyGroup, PackageAuditLevel, PackageManager } from './config';
3
+ import { DependencyGroupLong } from './runner/outdated/types';
3
4
  export declare const defaultAuditLevelMapping: Record<PackageAuditLevel, IssueSeverity>;
5
+ export declare const dependencyGroupToLong: Record<DependencyGroup, DependencyGroupLong>;
6
+ export declare const pkgManagerCommands: Record<PackageManager, string>;
4
7
  export declare const pkgManagerNames: Record<PackageManager, string>;
5
8
  export declare const pkgManagerIcons: Record<PackageManager, MaterialIcon>;
6
9
  export declare const pkgManagerDocs: Record<PackageManager, string>;
@@ -1,2 +1,6 @@
1
- import { PackageAuditLevel } from '../../config';
1
+ import { DependencyGroup, PackageAuditLevel, PackageManager } from '../../config';
2
+ import { AuditResult } from './types';
2
3
  export declare const auditScoreModifiers: Record<PackageAuditLevel, number>;
4
+ export declare const normalizeAuditMapper: Record<PackageManager, (output: string) => AuditResult>;
5
+ export declare const postProcessingAuditMapper: Partial<Record<PackageManager, (result: Record<DependencyGroup, AuditResult>) => Record<DependencyGroup, AuditResult>>>;
6
+ export declare const auditArgs: (groupDep: DependencyGroup) => Record<PackageManager, string[]>;
@@ -1,7 +1,7 @@
1
- import type { AuditOutput, Issue, IssueSeverity } from '@code-pushup/models';
2
- import { DependencyGroup, PackageAuditLevel } from '../../config';
3
- import { NpmAuditResultJson, Vulnerabilities } from './types';
4
- export declare function auditResultToAuditOutput(result: NpmAuditResultJson, dependenciesType: DependencyGroup, auditLevelMapping: Record<PackageAuditLevel, IssueSeverity>): AuditOutput;
5
- export declare function calculateAuditScore(stats: Record<PackageAuditLevel | 'total', number>): number;
6
- export declare function vulnerabilitiesToDisplayValue(vulnerabilities: Record<PackageAuditLevel | 'total', number>): string;
7
- export declare function vulnerabilitiesToIssues(vulnerabilities: Vulnerabilities, auditLevelMapping: Record<PackageAuditLevel, IssueSeverity>): Issue[];
1
+ import type { AuditOutput, Issue } from '@code-pushup/models';
2
+ import { AuditSeverity, DependencyGroup, PackageManager } from '../../config';
3
+ import { AuditResult, AuditSummary, Vulnerability } from './types';
4
+ export declare function auditResultToAuditOutput(result: AuditResult, packageManager: PackageManager, dependenciesType: DependencyGroup, auditLevelMapping: AuditSeverity): AuditOutput;
5
+ export declare function calculateAuditScore(stats: AuditSummary): number;
6
+ export declare function summaryToDisplayValue(summary: AuditSummary): string;
7
+ export declare function vulnerabilitiesToIssues(vulnerabilities: Vulnerability[], auditLevelMapping: AuditSeverity): Issue[];
@@ -1,27 +1,103 @@
1
1
  import type { PackageAuditLevel } from '../../config';
2
- type Advisory = {
2
+ export type Vulnerability = {
3
+ name: string;
4
+ id?: number;
5
+ title?: string;
6
+ url?: string;
7
+ severity: PackageAuditLevel;
8
+ versionRange: string;
9
+ directDependency: string | true;
10
+ fixInformation: string | false;
11
+ };
12
+ export type AuditSummary = Record<PackageAuditLevel | 'total', number>;
13
+ export type AuditResult = {
14
+ vulnerabilities: Vulnerability[];
15
+ summary: AuditSummary;
16
+ };
17
+ export type NpmAdvisory = {
3
18
  title: string;
4
19
  url: string;
5
20
  };
6
- type FixInformation = {
21
+ export type NpmFixInformation = {
7
22
  name: string;
8
23
  version: string;
9
24
  isSemVerMajor: boolean;
10
25
  };
11
- export type Vulnerability = {
26
+ export type NpmVulnerability = {
12
27
  name: string;
13
28
  severity: PackageAuditLevel;
14
- via: Advisory[] | string[];
29
+ isDirect: boolean;
30
+ effects: string[];
31
+ via: NpmAdvisory[] | string[];
15
32
  range: string;
16
- fixAvailable: boolean | FixInformation;
17
- };
18
- export type Vulnerabilities = {
19
- [key: string]: Vulnerability;
33
+ fixAvailable: boolean | NpmFixInformation;
20
34
  };
35
+ export type NpmVulnerabilities = Record<string, NpmVulnerability>;
21
36
  export type NpmAuditResultJson = {
22
- vulnerabilities: Vulnerabilities;
37
+ vulnerabilities: NpmVulnerabilities;
38
+ metadata: {
39
+ vulnerabilities: AuditSummary;
40
+ };
41
+ };
42
+ export type Yarnv1AuditAdvisory = {
43
+ type: 'auditAdvisory';
44
+ data: {
45
+ resolution: {
46
+ id: number;
47
+ path: string;
48
+ };
49
+ advisory: {
50
+ module_name: string;
51
+ severity: PackageAuditLevel;
52
+ vulnerable_versions: string;
53
+ recommendation: string;
54
+ title: string;
55
+ url: string;
56
+ };
57
+ };
58
+ };
59
+ export type Yarnv1AuditSummary = {
60
+ type: 'auditSummary';
61
+ data: {
62
+ vulnerabilities: Record<PackageAuditLevel, number>;
63
+ };
64
+ };
65
+ export type Yarnv1AuditResultJson = [
66
+ ...Yarnv1AuditAdvisory[],
67
+ Yarnv1AuditSummary
68
+ ];
69
+ export type Yarnv2AuditAdvisory = {
70
+ module_name: string;
71
+ severity: PackageAuditLevel;
72
+ vulnerable_versions: string;
73
+ recommendation: string;
74
+ title: string;
75
+ url: string;
76
+ findings: {
77
+ paths: string[];
78
+ }[];
79
+ };
80
+ export type Yarnv2AuditResultJson = {
81
+ advisories: Record<string, Yarnv2AuditAdvisory>;
82
+ metadata: {
83
+ vulnerabilities: Record<PackageAuditLevel, number>;
84
+ };
85
+ };
86
+ export type PnpmAuditAdvisory = {
87
+ module_name: string;
88
+ id: number;
89
+ severity: PackageAuditLevel;
90
+ vulnerable_versions: string;
91
+ recommendation: string;
92
+ title: string;
93
+ url: string;
94
+ findings: {
95
+ paths: string[];
96
+ }[];
97
+ };
98
+ export type PnpmAuditResultJson = {
99
+ advisories: Record<string, PnpmAuditAdvisory>;
23
100
  metadata: {
24
- vulnerabilities: Record<PackageAuditLevel | 'total', number>;
101
+ vulnerabilities: Record<PackageAuditLevel, number>;
25
102
  };
26
103
  };
27
- export {};
@@ -0,0 +1,8 @@
1
+ import { AuditResult, NpmAdvisory, NpmFixInformation, NpmVulnerabilities } from './types';
2
+ export declare function npmToAuditResult(output: string): AuditResult;
3
+ export declare function npmToFixInformation(fixAvailable: boolean | NpmFixInformation): string;
4
+ export declare function npmToAdvisory(name: string, vulnerabilities: NpmVulnerabilities, prevNodes?: Set<string>): NpmAdvisory | null;
5
+ export declare function yarnv1ToAuditResult(output: string): AuditResult;
6
+ export declare function yarnv2ToAuditResult(output: string): AuditResult;
7
+ export declare function pnpmToAuditResult(output: string): AuditResult;
8
+ export declare function pnpmToDirectDependency(path: string): string | true;
@@ -1,3 +1,6 @@
1
1
  import { IssueSeverity } from '@code-pushup/models';
2
- import { VersionType } from './types';
2
+ import { PackageManager } from '../../config';
3
+ import { OutdatedResult, VersionType } from './types';
3
4
  export declare const outdatedSeverity: Record<VersionType, IssueSeverity>;
5
+ export declare const normalizeOutdatedMapper: Record<PackageManager, (output: string) => OutdatedResult>;
6
+ export declare const outdatedArgs: Record<PackageManager, string[]>;
@@ -1,7 +1,7 @@
1
1
  import { Issue } from '@code-pushup/models';
2
- import { DependencyGroup } from '../../config';
3
- import { NormalizedOutdatedEntries, NpmOutdatedResultJson, PackageVersion, VersionType } from './types';
4
- export declare function outdatedResultToAuditOutput(result: NpmOutdatedResultJson, dependenciesType: DependencyGroup): {
2
+ import { DependencyGroup, PackageManager } from '../../config';
3
+ import { OutdatedResult, PackageVersion, VersionType } from './types';
4
+ export declare function outdatedResultToAuditOutput(result: OutdatedResult, packageManager: PackageManager, dependencyGroup: DependencyGroup): {
5
5
  details?: {
6
6
  issues: {
7
7
  message: string;
@@ -23,7 +23,7 @@ export declare function outdatedResultToAuditOutput(result: NpmOutdatedResultJso
23
23
  displayValue: string;
24
24
  };
25
25
  export declare function calculateOutdatedScore(majorOutdated: number, totalDeps: number): number;
26
- export declare function outdatedToDisplayValue(majorOutdated: number, totalOutdated: number): string;
27
- export declare function outdatedToIssues(dependencies: NormalizedOutdatedEntries): Issue[];
28
- export declare function getOutdatedLevel(currentFullVersion: string, wantedFullVersion: string): VersionType;
26
+ export declare function outdatedToDisplayValue(stats: Record<VersionType, number>): string;
27
+ export declare function outdatedToIssues(dependencies: OutdatedResult): Issue[];
28
+ export declare function getOutdatedLevel(currentFullVersion: string, latestFullVersion: string): VersionType;
29
29
  export declare function splitPackageVersion(fullVersion: string): PackageVersion;
@@ -1,15 +1,54 @@
1
- export type VersionType = 'major' | 'minor' | 'patch';
1
+ export declare const versionType: readonly ["major", "minor", "patch"];
2
+ export type VersionType = (typeof versionType)[number];
2
3
  export type PackageVersion = Record<VersionType, number>;
3
- export type VersionOverview = {
4
+ export type DependencyGroupLong = 'dependencies' | 'devDependencies' | 'optionalDependencies';
5
+ export type OutdatedResult = {
6
+ name: string;
7
+ current: string;
8
+ latest: string;
9
+ type: DependencyGroupLong;
10
+ url?: string;
11
+ }[];
12
+ export type NpmVersionOverview = {
4
13
  current?: string;
5
- wanted: string;
6
- type: 'dependencies' | 'devDependencies' | 'optionalDependencies';
14
+ latest: string;
15
+ type: DependencyGroupLong;
7
16
  homepage?: string;
8
17
  };
9
- export type NormalizedVersionOverview = Omit<VersionOverview, 'current'> & {
18
+ export type NpmNormalizedOverview = Omit<NpmVersionOverview, 'current'> & {
19
+ current: string;
20
+ };
21
+ export type NpmOutdatedResultJson = Record<string, NpmVersionOverview>;
22
+ export type Yarnv1VersionOverview = [
23
+ string,
24
+ string,
25
+ string,
26
+ string,
27
+ string,
28
+ DependencyGroupLong,
29
+ string
30
+ ];
31
+ type Yarnv1Info = {
32
+ type: 'info';
33
+ };
34
+ type Yarnv1Table = {
35
+ type: 'table';
36
+ data: {
37
+ body: Yarnv1VersionOverview[];
38
+ };
39
+ };
40
+ export type Yarnv1OutdatedResultJson = [Yarnv1Info, Yarnv1Table];
41
+ export type Yarnv2VersionOverview = {
10
42
  current: string;
43
+ latest: string;
44
+ name: string;
45
+ type: DependencyGroupLong;
11
46
  };
12
- export type NormalizedOutdatedEntries = [string, NormalizedVersionOverview][];
13
- export type NpmOutdatedResultJson = {
14
- [key: string]: VersionOverview;
47
+ export type Yarnv2OutdatedResultJson = Yarnv2VersionOverview[];
48
+ export type PnpmVersionOverview = {
49
+ current: string;
50
+ latest: string;
51
+ dependencyType: DependencyGroupLong;
15
52
  };
53
+ export type PnpmOutdatedResultJson = Record<string, PnpmVersionOverview>;
54
+ export {};
@@ -0,0 +1,5 @@
1
+ import { OutdatedResult } from './types';
2
+ export declare function npmToOutdatedResult(output: string): OutdatedResult;
3
+ export declare function yarnv1ToOutdatedResult(output: string): OutdatedResult;
4
+ export declare function yarnv2ToOutdatedResult(output: string): OutdatedResult;
5
+ export declare function pnpmToOutdatedResult(output: string): OutdatedResult;
@@ -0,0 +1,2 @@
1
+ import { AuditResult, Vulnerability } from './audit/types';
2
+ export declare function filterAuditResult(result: AuditResult, key: keyof Vulnerability, referenceResult?: AuditResult): AuditResult;