1use serde_json::{Map, Value};
2
3#[derive(Debug, Clone)]
5pub struct SortOptions {
6 pub pretty: bool,
8}
9
10impl Default for SortOptions {
11 fn default() -> Self {
12 Self { pretty: true }
13 }
14}
15
16pub fn sort_package_json_with_options(
18 input: &str,
19 options: &SortOptions,
20) -> Result<String, serde_json::Error> {
21 let value: Value = serde_json::from_str(input)?;
22
23 let sorted_value =
24 if let Value::Object(obj) = value { Value::Object(sort_object_keys(obj)) } else { value };
25
26 let result = if options.pretty {
27 let mut s = serde_json::to_string_pretty(&sorted_value)?;
28 s.push('\n');
29 s
30 } else {
31 serde_json::to_string(&sorted_value)?
32 };
33
34 Ok(result)
35}
36
37pub fn sort_package_json(input: &str) -> Result<String, serde_json::Error> {
39 sort_package_json_with_options(input, &SortOptions::default())
40}
41
42macro_rules! declare_field_order {
69 (
70 $key:ident, $value:ident, $known:ident, $non_private:ident, $private:ident;
71 [
72 $( $idx:literal => $field_name:literal $( => $transform:expr )? ),* $(,)?
73 ]
74 ) => {
75 {
76 $( let _ = $idx; )*
78
79 match $key.as_str() {
81 $(
82 $field_name => {
83 $known.push((
84 $idx,
85 $key,
86 declare_field_order!(@value $value $(, $transform)?)
87 ));
88 },
89 )*
90 _ => {
91 if $key.starts_with('_') {
93 $private.push(($key, $value));
94 } else {
95 $non_private.push(($key, $value));
96 }
97 }
98 }
99 }
100 };
101
102 (@value $value:ident) => { $value };
104
105 (@value $value:ident, $transform:expr) => { $transform };
107}
108
109fn transform_value<F>(value: &Value, transform: F) -> Value
110where
111 F: FnOnce(&Map<String, Value>) -> Map<String, Value>,
112{
113 match value {
114 Value::Object(o) => Value::Object(transform(o)),
115 _ => value.clone(),
116 }
117}
118
119fn transform_array<F>(value: &Value, transform: F) -> Value
120where
121 F: FnOnce(&[Value]) -> Vec<Value>,
122{
123 match value {
124 Value::Array(arr) => Value::Array(transform(arr)),
125 _ => value.clone(),
126 }
127}
128
129fn transform_with_key_order(value: &Value, key_order: &[&str]) -> Value {
130 transform_value(value, |o| sort_object_by_key_order(o, key_order))
131}
132
133fn transform_people_array(value: &Value) -> Value {
134 transform_array(value, |arr| {
135 arr.iter()
136 .map(|v| match v {
137 Value::Object(o) => Value::Object(sort_people_object(o)),
138 _ => v.clone(),
139 })
140 .collect()
141 })
142}
143
144fn sort_object_alphabetically(obj: &Map<String, Value>) -> Map<String, Value> {
145 let mut keys: Vec<&String> = obj.keys().collect();
146 keys.sort();
147
148 let mut result = Map::new();
149 for key in keys {
150 if let Some(value) = obj.get(key) {
151 result.insert(key.clone(), value.clone());
152 }
153 }
154 result
155}
156
157fn sort_object_recursive(obj: &Map<String, Value>) -> Map<String, Value> {
158 let mut keys: Vec<&String> = obj.keys().collect();
159 keys.sort();
160
161 let mut result = Map::new();
162 for key in keys {
163 if let Some(value) = obj.get(key) {
164 let transformed_value = match value {
165 Value::Object(nested) => Value::Object(sort_object_recursive(nested)),
166 _ => value.clone(),
167 };
168 result.insert(key.clone(), transformed_value);
169 }
170 }
171 result
172}
173
174fn sort_array_unique(arr: &[Value]) -> Vec<Value> {
175 let mut strings: Vec<String> =
176 arr.iter().filter_map(|v| v.as_str().map(String::from)).collect();
177
178 strings.sort();
179 strings.dedup();
180
181 strings.into_iter().map(Value::String).collect()
182}
183
184fn sort_object_by_key_order(obj: &Map<String, Value>, key_order: &[&str]) -> Map<String, Value> {
185 let mut result = Map::new();
186
187 for &key in key_order {
189 if let Some(value) = obj.get(key) {
190 result.insert(key.to_string(), value.clone());
191 }
192 }
193
194 let mut remaining: Vec<&String> =
196 obj.keys().filter(|k| !key_order.contains(&k.as_str())).collect();
197 remaining.sort();
198
199 for key in remaining {
200 if let Some(value) = obj.get(key) {
201 result.insert(key.clone(), value.clone());
202 }
203 }
204
205 result
206}
207
208fn sort_people_object(obj: &Map<String, Value>) -> Map<String, Value> {
209 sort_object_by_key_order(obj, &["name", "email", "url"])
210}
211
212fn sort_exports(obj: &Map<String, Value>) -> Map<String, Value> {
213 let mut paths = Vec::new();
214 let mut types_conds = Vec::new();
215 let mut other_conds = Vec::new();
216 let mut default_cond = None;
217
218 for (key, value) in obj.iter() {
219 if key.starts_with('.') {
220 paths.push(key);
221 } else if key == "default" {
222 default_cond = Some((key, value));
223 } else if key == "types" || key.starts_with("types@") {
224 types_conds.push(key);
225 } else {
226 other_conds.push(key);
227 }
228 }
229
230 paths.sort();
232 types_conds.sort();
233 other_conds.sort();
234
235 let mut result = Map::new();
236
237 for key in paths {
239 if let Some(value) = obj.get(key) {
240 let transformed = match value {
241 Value::Object(nested) => Value::Object(sort_exports(nested)),
242 _ => value.clone(),
243 };
244 result.insert(key.clone(), transformed);
245 }
246 }
247
248 for key in types_conds {
249 if let Some(value) = obj.get(key) {
250 result.insert(key.clone(), value.clone());
251 }
252 }
253
254 for key in other_conds {
255 if let Some(value) = obj.get(key) {
256 result.insert(key.clone(), value.clone());
257 }
258 }
259
260 if let Some((key, value)) = default_cond {
261 result.insert(key.clone(), value.clone());
262 }
263
264 result
265}
266
267fn sort_object_keys(obj: Map<String, Value>) -> Map<String, Value> {
268 let mut known: Vec<(usize, String, Value)> = Vec::new(); let mut non_private: Vec<(String, Value)> = Vec::new();
271 let mut private: Vec<(String, Value)> = Vec::new();
272
273 for (key, value) in obj {
275 declare_field_order!(key, value, known, non_private, private; [
276 0 => "$schema",
277 1 => "name",
278 2 => "displayName",
279 3 => "version",
280 4 => "stableVersion",
281 5 => "private",
282 6 => "description",
283 7 => "categories" => transform_array(&value, sort_array_unique),
284 8 => "keywords" => transform_array(&value, sort_array_unique),
285 9 => "homepage",
286 10 => "bugs" => transform_with_key_order(&value, &["url", "email"]),
287 11 => "repository" => transform_with_key_order(&value, &["type", "url"]),
288 12 => "author" => transform_value(&value, sort_people_object),
289 13 => "maintainers" => transform_people_array(&value),
290 14 => "contributors" => transform_people_array(&value),
291 15 => "donate" => transform_with_key_order(&value, &["type", "url"]),
292 16 => "funding" => transform_with_key_order(&value, &["type", "url"]),
293 17 => "sponsor" => transform_with_key_order(&value, &["type", "url"]),
294 18 => "license",
295 19 => "qna",
296 20 => "publisher",
297 21 => "sideEffects",
298 22 => "type",
299 23 => "main",
300 24 => "imports",
301 25 => "exports" => transform_value(&value, sort_exports),
302 26 => "svelte",
303 27 => "umd:main",
304 28 => "jsdelivr",
305 29 => "unpkg",
306 30 => "module",
307 31 => "esnext",
308 32 => "es2020",
309 33 => "esm2020",
310 34 => "fesm2020",
311 35 => "es2015",
312 36 => "esm2015",
313 37 => "fesm2015",
314 38 => "es5",
315 39 => "esm5",
316 40 => "fesm5",
317 41 => "source",
318 42 => "jsnext:main",
319 43 => "browser",
320 44 => "umd",
321 45 => "react-native",
322 46 => "types",
323 47 => "typesVersions",
324 48 => "typings",
325 49 => "style",
326 50 => "example",
327 51 => "examplestyle",
328 52 => "assets",
329 53 => "bin" => transform_value(&value, sort_object_alphabetically),
330 54 => "man",
331 55 => "directories" => transform_with_key_order(
332 &value,
333 &["lib", "bin", "man", "doc", "example", "test"],
334 ),
335 56 => "files" => transform_array(&value, sort_array_unique),
336 57 => "workspaces",
337 58 => "binary" => transform_with_key_order(
338 &value,
339 &["module_name", "module_path", "remote_path", "package_name", "host"],
340 ),
341 59 => "scripts",
342 60 => "betterScripts",
343 61 => "l10n",
344 62 => "contributes",
345 63 => "activationEvents" => transform_array(&value, sort_array_unique),
346 64 => "husky" => transform_value(&value, sort_object_recursive),
347 65 => "simple-git-hooks",
348 66 => "pre-commit",
349 67 => "commitlint" => transform_value(&value, sort_object_recursive),
350 68 => "lint-staged",
351 69 => "nano-staged",
352 70 => "resolutions" => transform_value(&value, sort_object_alphabetically),
353 71 => "overrides" => transform_value(&value, sort_object_alphabetically),
354 72 => "dependencies" => transform_value(&value, sort_object_alphabetically),
355 73 => "devDependencies" => transform_value(&value, sort_object_alphabetically),
356 74 => "dependenciesMeta",
357 75 => "peerDependencies" => transform_value(&value, sort_object_alphabetically),
358 76 => "peerDependenciesMeta",
359 77 => "optionalDependencies" => transform_value(&value, sort_object_alphabetically),
360 78 => "bundledDependencies" => transform_array(&value, sort_array_unique),
361 79 => "bundleDependencies" => transform_array(&value, sort_array_unique),
362 80 => "napi" => transform_value(&value, sort_object_alphabetically),
363 81 => "extensionPack" => transform_array(&value, sort_array_unique),
364 82 => "extensionDependencies" => transform_array(&value, sort_array_unique),
365 83 => "extensionKind" => transform_array(&value, sort_array_unique),
366 84 => "flat",
367 85 => "packageManager",
368 86 => "config" => transform_value(&value, sort_object_alphabetically),
369 87 => "nodemonConfig" => transform_value(&value, sort_object_recursive),
370 88 => "browserify" => transform_value(&value, sort_object_recursive),
371 89 => "babel" => transform_value(&value, sort_object_recursive),
372 90 => "browserslist",
373 91 => "xo" => transform_value(&value, sort_object_recursive),
374 92 => "prettier" => transform_value(&value, sort_object_recursive),
375 93 => "eslintConfig" => transform_value(&value, sort_object_recursive),
376 94 => "eslintIgnore",
377 95 => "npmpkgjsonlint",
378 96 => "npmPackageJsonLintConfig",
379 97 => "npmpackagejsonlint",
380 98 => "release",
381 99 => "remarkConfig" => transform_value(&value, sort_object_recursive),
382 100 => "stylelint" => transform_value(&value, sort_object_recursive),
383 101 => "ava" => transform_value(&value, sort_object_recursive),
384 102 => "jest" => transform_value(&value, sort_object_recursive),
385 103 => "jest-junit",
386 104 => "jest-stare",
387 105 => "mocha" => transform_value(&value, sort_object_recursive),
388 106 => "nyc" => transform_value(&value, sort_object_recursive),
389 107 => "c8" => transform_value(&value, sort_object_recursive),
390 108 => "tap",
391 109 => "oclif" => transform_value(&value, sort_object_recursive),
392 110 => "engines" => transform_value(&value, sort_object_alphabetically),
393 111 => "engineStrict",
394 112 => "volta" => transform_value(&value, sort_object_recursive),
395 113 => "languageName",
396 114 => "os",
397 115 => "cpu",
398 116 => "libc" => transform_array(&value, sort_array_unique),
399 117 => "devEngines" => transform_value(&value, sort_object_alphabetically),
400 118 => "preferGlobal",
401 119 => "publishConfig" => transform_value(&value, sort_object_alphabetically),
402 120 => "icon",
403 121 => "badges",
404 122 => "galleryBanner",
405 123 => "preview",
406 124 => "markdown",
407 125 => "pnpm",
408 ]);
409 }
410
411 known.sort_unstable_by_key(|(index, _, _)| *index);
413 non_private.sort_unstable_by(|(a, _), (b, _)| a.cmp(b));
414 private.sort_unstable_by(|(a, _), (b, _)| a.cmp(b));
415
416 let mut result = Map::new();
418
419 for (_index, key, value) in known {
421 result.insert(key, value);
422 }
423
424 for (key, value) in non_private {
426 result.insert(key, value);
427 }
428
429 for (key, value) in private {
431 result.insert(key, value);
432 }
433
434 result
435}