|
| 1 | +/** |
| 2 | + * Parse a list of CLI package strings into the expected HighlightPackages format expected |
| 3 | + * by @nodesecure/scanner: `string[] | Record<string, string[] | SemverRange>`. |
| 4 | + * |
| 5 | + * Each input string can be: |
| 6 | + * - "lodash" → plain name, no version constraint |
| 7 | + * - "lodash@^4.0.0" → name with a semver range |
| 8 | + * - "lodash@1.0.0,2.0.0" → name with a list of specific versions |
| 9 | + * - "@scope/pkg" → scoped package, no version constraint |
| 10 | + * - "@scope/pkg@^1.0.0" → scoped package with a semver range |
| 11 | + * |
| 12 | + * When none of the entries carry a version constraint the function returns a plain `string[]`. |
| 13 | + * If at least one entry has a version constraint the function returns a `Record`; |
| 14 | + * Entries without a constraint are mapped to '*' |
| 15 | + * |
| 16 | + * @param {string | string[]} input |
| 17 | + * @returns {string[] | Record<string, string[] | string>} |
| 18 | + */ |
| 19 | +export function parsePackages(input) { |
| 20 | + const items = Array.isArray(input) ? input : [input]; |
| 21 | + const parsed = items.map(parseOne); |
| 22 | + |
| 23 | + const hasVersionConstraints = parsed.some(({ version }) => version !== null); |
| 24 | + |
| 25 | + if (!hasVersionConstraints) { |
| 26 | + return parsed.map(({ name }) => name); |
| 27 | + } |
| 28 | + |
| 29 | + return Object.fromEntries( |
| 30 | + parsed.map(({ name, version }) => [name, version ?? "*"]) |
| 31 | + ); |
| 32 | +} |
| 33 | + |
| 34 | +/** |
| 35 | + * @param {string} str |
| 36 | + * @returns {{ name: string, version: string | string[] | null }} |
| 37 | + */ |
| 38 | +function parseOne(str) { |
| 39 | + // Scoped packages start with "@", so search for a second "@" after index 1. |
| 40 | + const versionSeparator = str.startsWith("@") ? str.indexOf("@", 1) : str.indexOf("@"); |
| 41 | + |
| 42 | + if (versionSeparator === -1) { |
| 43 | + return { name: str.trim(), version: null }; |
| 44 | + } |
| 45 | + |
| 46 | + const name = str.slice(0, versionSeparator).trim(); |
| 47 | + const versionStr = str.slice(versionSeparator + 1).trim(); |
| 48 | + |
| 49 | + if (versionStr === "") { |
| 50 | + return { name, version: null }; |
| 51 | + } |
| 52 | + |
| 53 | + if (versionStr.includes(",")) { |
| 54 | + const versions = versionStr.split(",").map((v) => v.trim()).filter(Boolean); |
| 55 | + let version; |
| 56 | + if (versions.length === 0) { |
| 57 | + version = null; |
| 58 | + } |
| 59 | + else if (versions.length === 1) { |
| 60 | + version = versions[0]; |
| 61 | + } |
| 62 | + else { |
| 63 | + version = versions; |
| 64 | + } |
| 65 | + |
| 66 | + return { name, version }; |
| 67 | + } |
| 68 | + |
| 69 | + return { name, version: versionStr }; |
| 70 | +} |
0 commit comments