Skip to repository content203 lines · 7.1 KB · rust
tenant.openagents/omega
No repository description is available.
OpenAgents Git authority 2026-07-28T02:33:53.158Z Public web read
NIP-34 coordinate
30617:7649603503856e5148d571eac2766b288a8ff1e9e35d380337a1d2b0015b4f92:omegaMaintainersHidden in public view
References2 branches · 1 tag
Read-only clone
git clone https://openagents.com/git/tenant.openagents/omega.gitBrowse files
package_json.rs
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 bun_package_path: Option<Arc<Path>>,
19 pub node_package_path: Option<Arc<Path>>,
20 pub scripts: BTreeSet<(Arc<Path>, String)>,
21 pub package_manager: Option<&'static str>,
22}
23
24impl PackageJsonData {
25 pub fn new(path: Arc<Path>, package_json: HashMap<String, Value>) -> Self {
26 let mut scripts = BTreeSet::new();
27 if let Some(Value::Object(package_json_scripts)) = package_json.get("scripts") {
28 scripts.extend(
29 package_json_scripts
30 .keys()
31 .cloned()
32 .map(|name| (path.clone(), name)),
33 );
34 }
35
36 let mut jest_package_path = None;
37 let mut mocha_package_path = None;
38 let mut vitest_package_path = None;
39 let mut jasmine_package_path = None;
40 let mut bun_package_path = None;
41 let mut node_package_path = None;
42 if let Some(Value::Object(dependencies)) = package_json.get("devDependencies") {
43 if dependencies.contains_key("jest") {
44 jest_package_path.get_or_insert_with(|| path.clone());
45 }
46 if dependencies.contains_key("mocha") {
47 mocha_package_path.get_or_insert_with(|| path.clone());
48 }
49 if dependencies.contains_key("vitest") {
50 vitest_package_path.get_or_insert_with(|| path.clone());
51 }
52 if dependencies.contains_key("jasmine") {
53 jasmine_package_path.get_or_insert_with(|| path.clone());
54 }
55 if dependencies.contains_key("@types/bun") {
56 bun_package_path.get_or_insert_with(|| path.clone());
57 }
58 if dependencies.contains_key("@types/node") {
59 node_package_path.get_or_insert_with(|| path.clone());
60 }
61 }
62 if let Some(Value::Object(dev_dependencies)) = package_json.get("dependencies") {
63 if dev_dependencies.contains_key("jest") {
64 jest_package_path.get_or_insert_with(|| path.clone());
65 }
66 if dev_dependencies.contains_key("mocha") {
67 mocha_package_path.get_or_insert_with(|| path.clone());
68 }
69 if dev_dependencies.contains_key("vitest") {
70 vitest_package_path.get_or_insert_with(|| path.clone());
71 }
72 if dev_dependencies.contains_key("jasmine") {
73 jasmine_package_path.get_or_insert_with(|| path.clone());
74 }
75 if dev_dependencies.contains_key("@types/bun") {
76 bun_package_path.get_or_insert_with(|| path.clone());
77 }
78 if dev_dependencies.contains_key("@types/node") {
79 node_package_path.get_or_insert_with(|| path.clone());
80 }
81 }
82
83 let package_manager = package_manager_from_package_json(&package_json);
84
85 Self {
86 jest_package_path,
87 mocha_package_path,
88 vitest_package_path,
89 jasmine_package_path,
90 bun_package_path,
91 node_package_path,
92 scripts,
93 package_manager,
94 }
95 }
96
97 pub fn merge(&mut self, other: Self) {
98 self.jest_package_path = self.jest_package_path.take().or(other.jest_package_path);
99 self.mocha_package_path = self.mocha_package_path.take().or(other.mocha_package_path);
100 self.vitest_package_path = self
101 .vitest_package_path
102 .take()
103 .or(other.vitest_package_path);
104 self.jasmine_package_path = self
105 .jasmine_package_path
106 .take()
107 .or(other.jasmine_package_path);
108 self.bun_package_path = self.bun_package_path.take().or(other.bun_package_path);
109 self.node_package_path = self.node_package_path.take().or(other.node_package_path);
110 self.scripts.extend(other.scripts);
111 self.package_manager = self.package_manager.or(other.package_manager);
112 }
113}
114
115fn package_manager_from_package_json(
116 package_json: &HashMap<String, Value>,
117) -> Option<&'static str> {
118 package_json
119 .get("packageManager")
120 .and_then(|value| value.as_str())
121 .and_then(package_manager_name)
122 .or_else(|| {
123 package_json
124 .get("devEngines")
125 .and_then(|value| value.as_object())
126 .and_then(|dev_engines| dev_engines.get("packageManager"))
127 .and_then(package_manager_from_dev_engine)
128 })
129}
130
131fn package_manager_from_dev_engine(value: &Value) -> Option<&'static str> {
132 match value {
133 Value::Object(package_manager) => package_manager
134 .get("name")
135 .and_then(|name| name.as_str())
136 .and_then(package_manager_name),
137 Value::Array(package_managers) => package_managers
138 .iter()
139 .find_map(package_manager_from_dev_engine),
140 _ => None,
141 }
142}
143
144fn package_manager_name(value: &str) -> Option<&'static str> {
145 let value = value.split_once('@').map(|(name, _)| name).unwrap_or(value);
146 match value {
147 "pnpm" => Some("pnpm"),
148 "yarn" => Some("yarn"),
149 "npm" => Some("npm"),
150 "bun" => Some("bun"),
151 _ => None,
152 }
153}
154
155#[cfg(test)]
156mod tests {
157 use std::path::Path;
158
159 use super::PackageJsonData;
160
161 #[test]
162 fn package_manager_detection() {
163 fn package_manager(source: &str) -> Option<&'static str> {
164 PackageJsonData::new(
165 Path::new("/root/package.json").into(),
166 serde_json_lenient::from_str(source).expect("provided source should be valid JSON"),
167 )
168 .package_manager
169 }
170
171 assert_eq!(
172 package_manager(r#"{"packageManager": "pnpm@11.1.3"}"#),
173 Some("pnpm")
174 );
175
176 assert_eq!(
177 package_manager(
178 r#"{"devEngines": {"packageManager": {"name": "pnpm", "version": "^11.1.3", "onFail": "download"}}}"#
179 ),
180 Some("pnpm"),
181 );
182
183 assert_eq!(
184 package_manager(
185 r#"{"devEngines": {"packageManager": [{"name": "foo"}, {"name": "yarn", "version": "^4.0.0"}]}}"#
186 ),
187 Some("yarn"),
188 );
189
190 assert_eq!(
191 package_manager(
192 r#"{"packageManager": "npm@10.0.0", "devEngines": {"packageManager": {"name": "pnpm"}}}"#
193 ),
194 Some("npm"),
195 );
196
197 assert_eq!(
198 package_manager(r#"{"devEngines": {"packageManager": {"version": "^11.1.3"}}}"#),
199 None,
200 );
201 }
202}
203