1use crate::{Package, PackageMetadata};
2use anyhow::Result;
3use serde::Deserialize;
4use std::collections::BTreeMap;
5
6#[derive(Deserialize, Debug, PartialEq, Eq)]
7struct NpmLock {
8 packages: BTreeMap<String, PackageMetadata>,
9 name: String,
10 version: String,
11}
12
13impl NpmLock {
14 pub(crate) fn packages(self) -> Vec<Package> {
15 let mut packages: Vec<_> = self
16 .packages
17 .into_iter()
18 .filter(|(k, _)| !k.is_empty())
19 .map(|(k, v)| Package::new(&k.replace("node_modules/", ""), &v.version))
20 .collect();
21 packages.insert(0, Package::new(&self.name, &self.version));
22 packages
23 }
24}
25
26pub(crate) fn parse_npm_lock(contents: &str) -> Result<Vec<Package>> {
27 let npm_lock: NpmLock = serde_json::from_str(contents)?;
28 Ok(npm_lock.packages())
29}
30
31pub(crate) fn parse_yarn_lock(contents: &str) -> Result<Vec<Package>> {
32 let lock = yarn_lock_parser::parse_str(contents)?;
33 Ok(lock
34 .entries
35 .iter()
36 .map(|entry| Package::new(entry.name, entry.version))
37 .collect())
38}
39
40#[cfg(test)]
41mod tests {
42
43 use super::*;
44
45 #[test]
46 fn test_npm_lock() {
47 let contents = r#"
48{
49 "name": "my-proj",
50 "version": "0.0.0",
51 "lockfileVersion": 3,
52 "requires": true,
53 "packages": {
54 "": {
55 "name": "my-proj",
56 "version": "0.0.0",
57 "dependencies": {
58 "@eslint": "^2.1.2"
59 }
60 },
61 "node_modules/@eslint/eslintrc": {
62 "version": "2.1.2",
63 "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.2.tgz"
64 }
65 }
66}
67"#;
68
69 let packages = parse_npm_lock(contents).unwrap();
70 assert_eq!(
71 &packages,
72 &[
73 Package::new("my-proj", "0.0.0"),
74 Package::new("@eslint/eslintrc", "2.1.2")
75 ]
76 );
77 }
78
79 #[test]
80 fn test_yarn_lock() {
81 let contents = r#"
82# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
83# yarn lockfile v1
84
85
86leftpad@^0.0.1:
87 version "0.0.1"
88 resolved "https://registry.yarnpkg.com/leftpad/-/leftpad-0.0.1.tgz#86b1a4de4face180ac545a83f1503523d8fed115"
89 integrity sha512-kBAuxBQJlJ85LDc+SnGSX6gWJnJR9Qk4lbgXmz/qPfCOCieCk7BgoN3YvzoNr5BUjqxQDOQxawJJvXXd6c+6Mg==
90
91typescript@^4.9.5:
92 version "4.9.5"
93 resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a"
94 integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==
95 "#;
96 let contents = contents.trim();
97 let packages = parse_yarn_lock(contents).unwrap();
98 assert_eq!(
99 packages,
100 &[
101 Package::new("leftpad", "0.0.1"),
102 Package::new("typescript", "4.9.5"),
103 ]
104 );
105 }
106}