1use chrono::{DateTime, Local};
2use collections::{BTreeSet, HashMap};
3use serde_json_lenient::Value;
4use std::{path::Path, sync::Arc};
5
6#[derive(Clone, Debug)]
7pub struct PackageJson {
8 pub mtime: DateTime<Local>,
9 pub data: PackageJsonData,
10}
11
12#[derive(Clone, Debug, Default, PartialEq, Eq)]
13pub struct PackageJsonData {
14 pub jest_package_path: Option<Arc<Path>>,
15 pub mocha_package_path: Option<Arc<Path>>,
16 pub vitest_package_path: Option<Arc<Path>>,
17 pub jasmine_package_path: Option<Arc<Path>>,
18 pub scripts: BTreeSet<(Arc<Path>, String)>,
19 pub package_manager: Option<&'static str>,
20}
21
22impl PackageJsonData {
23 pub fn new(path: Arc<Path>, package_json: HashMap<String, Value>) -> Self {
24 let mut scripts = BTreeSet::new();
25 if let Some(Value::Object(package_json_scripts)) = package_json.get("scripts") {
26 scripts.extend(
27 package_json_scripts
28 .keys()
29 .cloned()
30 .map(|name| (path.clone(), name)),
31 );
32 }
33
34 let mut jest_package_path = None;
35 let mut mocha_package_path = None;
36 let mut vitest_package_path = None;
37 let mut jasmine_package_path = None;
38 if let Some(Value::Object(dependencies)) = package_json.get("devDependencies") {
39 if dependencies.contains_key("jest") {
40 jest_package_path.get_or_insert_with(|| path.clone());
41 }
42 if dependencies.contains_key("mocha") {
43 mocha_package_path.get_or_insert_with(|| path.clone());
44 }
45 if dependencies.contains_key("vitest") {
46 vitest_package_path.get_or_insert_with(|| path.clone());
47 }
48 if dependencies.contains_key("jasmine") {
49 jasmine_package_path.get_or_insert_with(|| path.clone());
50 }
51 }
52 if let Some(Value::Object(dev_dependencies)) = package_json.get("dependencies") {
53 if dev_dependencies.contains_key("jest") {
54 jest_package_path.get_or_insert_with(|| path.clone());
55 }
56 if dev_dependencies.contains_key("mocha") {
57 mocha_package_path.get_or_insert_with(|| path.clone());
58 }
59 if dev_dependencies.contains_key("vitest") {
60 vitest_package_path.get_or_insert_with(|| path.clone());
61 }
62 if dev_dependencies.contains_key("jasmine") {
63 jasmine_package_path.get_or_insert_with(|| path.clone());
64 }
65 }
66
67 let package_manager = package_json
68 .get("packageManager")
69 .and_then(|value| value.as_str())
70 .and_then(|value| {
71 if value.starts_with("pnpm") {
72 Some("pnpm")
73 } else if value.starts_with("yarn") {
74 Some("yarn")
75 } else if value.starts_with("npm") {
76 Some("npm")
77 } else {
78 None
79 }
80 });
81
82 Self {
83 jest_package_path,
84 mocha_package_path,
85 vitest_package_path,
86 jasmine_package_path,
87 scripts,
88 package_manager,
89 }
90 }
91
92 pub fn merge(&mut self, other: Self) {
93 self.jest_package_path = self.jest_package_path.take().or(other.jest_package_path);
94 self.mocha_package_path = self.mocha_package_path.take().or(other.mocha_package_path);
95 self.vitest_package_path = self
96 .vitest_package_path
97 .take()
98 .or(other.vitest_package_path);
99 self.jasmine_package_path = self
100 .jasmine_package_path
101 .take()
102 .or(other.jasmine_package_path);
103 self.scripts.extend(other.scripts);
104 self.package_manager = self.package_manager.or(other.package_manager);
105 }
106}