lockdiff/
javascript.rs

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}