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
42fn sort_object_alphabetically(obj: &Map<String, Value>) -> Map<String, Value> {
43 let mut keys: Vec<&String> = obj.keys().collect();
44 keys.sort();
45
46 let mut result = Map::new();
47 for key in keys {
48 if let Some(value) = obj.get(key) {
49 result.insert(key.clone(), value.clone());
50 }
51 }
52 result
53}
54
55fn sort_object_recursive(obj: &Map<String, Value>) -> Map<String, Value> {
56 let mut keys: Vec<&String> = obj.keys().collect();
57 keys.sort();
58
59 let mut result = Map::new();
60 for key in keys {
61 if let Some(value) = obj.get(key) {
62 let transformed_value = match value {
63 Value::Object(nested) => Value::Object(sort_object_recursive(nested)),
64 _ => value.clone(),
65 };
66 result.insert(key.clone(), transformed_value);
67 }
68 }
69 result
70}
71
72fn sort_array_unique(arr: &[Value]) -> Vec<Value> {
73 let mut strings: Vec<String> =
74 arr.iter().filter_map(|v| v.as_str().map(String::from)).collect();
75
76 strings.sort();
77 strings.dedup();
78
79 strings.into_iter().map(Value::String).collect()
80}
81
82fn sort_object_by_key_order(obj: &Map<String, Value>, key_order: &[&str]) -> Map<String, Value> {
83 let mut result = Map::new();
84
85 for &key in key_order {
87 if let Some(value) = obj.get(key) {
88 result.insert(key.to_string(), value.clone());
89 }
90 }
91
92 let mut remaining: Vec<&String> =
94 obj.keys().filter(|k| !key_order.contains(&k.as_str())).collect();
95 remaining.sort();
96
97 for key in remaining {
98 if let Some(value) = obj.get(key) {
99 result.insert(key.clone(), value.clone());
100 }
101 }
102
103 result
104}
105
106fn sort_people_object(obj: &Map<String, Value>) -> Map<String, Value> {
107 sort_object_by_key_order(obj, &["name", "email", "url"])
108}
109
110fn sort_exports(obj: &Map<String, Value>) -> Map<String, Value> {
111 let mut paths = Vec::new();
112 let mut types_conds = Vec::new();
113 let mut other_conds = Vec::new();
114 let mut default_cond = None;
115
116 for (key, value) in obj.iter() {
117 if key.starts_with('.') {
118 paths.push(key);
119 } else if key == "default" {
120 default_cond = Some((key, value));
121 } else if key == "types" || key.starts_with("types@") {
122 types_conds.push(key);
123 } else {
124 other_conds.push(key);
125 }
126 }
127
128 paths.sort();
130 types_conds.sort();
131 other_conds.sort();
132
133 let mut result = Map::new();
134
135 for key in paths {
137 if let Some(value) = obj.get(key) {
138 let transformed = match value {
139 Value::Object(nested) => Value::Object(sort_exports(nested)),
140 _ => value.clone(),
141 };
142 result.insert(key.clone(), transformed);
143 }
144 }
145
146 for key in types_conds {
147 if let Some(value) = obj.get(key) {
148 result.insert(key.clone(), value.clone());
149 }
150 }
151
152 for key in other_conds {
153 if let Some(value) = obj.get(key) {
154 result.insert(key.clone(), value.clone());
155 }
156 }
157
158 if let Some((key, value)) = default_cond {
159 result.insert(key.clone(), value.clone());
160 }
161
162 result
163}
164
165fn sort_object_keys(obj: Map<String, Value>) -> Map<String, Value> {
166 let mut known: Vec<(usize, String, Value)> = Vec::new(); let mut non_private: Vec<(String, Value)> = Vec::new();
169 let mut private: Vec<(String, Value)> = Vec::new();
170
171 for (key, value) in obj {
173 match key.as_str() {
174 "$schema" => known.push((0, key, value)),
175 "name" => known.push((1, key, value)),
176 "displayName" => known.push((2, key, value)),
177 "version" => known.push((3, key, value)),
178 "stableVersion" => known.push((4, key, value)),
179 "private" => known.push((5, key, value)),
180 "description" => known.push((6, key, value)),
181 "categories" => {
182 let transformed = match &value {
183 Value::Array(arr) => Value::Array(sort_array_unique(arr)),
184 _ => value,
185 };
186 known.push((7, key, transformed));
187 }
188 "keywords" => {
189 let transformed = match &value {
190 Value::Array(arr) => Value::Array(sort_array_unique(arr)),
191 _ => value,
192 };
193 known.push((8, key, transformed));
194 }
195 "homepage" => known.push((9, key, value)),
196 "bugs" => {
197 let transformed = match &value {
198 Value::Object(o) => {
199 Value::Object(sort_object_by_key_order(o, &["url", "email"]))
200 }
201 _ => value,
202 };
203 known.push((10, key, transformed));
204 }
205 "repository" => {
206 let transformed = match &value {
207 Value::Object(o) => {
208 Value::Object(sort_object_by_key_order(o, &["type", "url"]))
209 }
210 _ => value,
211 };
212 known.push((11, key, transformed));
213 }
214 "author" => {
215 let transformed = match &value {
216 Value::Object(o) => Value::Object(sort_people_object(o)),
217 _ => value,
218 };
219 known.push((12, key, transformed));
220 }
221 "maintainers" => {
222 let transformed = match &value {
223 Value::Array(arr) => {
224 let people: Vec<Value> = arr
225 .iter()
226 .map(|v| match v {
227 Value::Object(o) => Value::Object(sort_people_object(o)),
228 _ => v.clone(),
229 })
230 .collect();
231 Value::Array(people)
232 }
233 _ => value,
234 };
235 known.push((13, key, transformed));
236 }
237 "contributors" => {
238 let transformed = match &value {
239 Value::Array(arr) => {
240 let people: Vec<Value> = arr
241 .iter()
242 .map(|v| match v {
243 Value::Object(o) => Value::Object(sort_people_object(o)),
244 _ => v.clone(),
245 })
246 .collect();
247 Value::Array(people)
248 }
249 _ => value,
250 };
251 known.push((14, key, transformed));
252 }
253 "donate" => {
254 let transformed = match &value {
255 Value::Object(o) => {
256 Value::Object(sort_object_by_key_order(o, &["type", "url"]))
257 }
258 _ => value,
259 };
260 known.push((15, key, transformed));
261 }
262 "funding" => {
263 let transformed = match &value {
264 Value::Object(o) => {
265 Value::Object(sort_object_by_key_order(o, &["type", "url"]))
266 }
267 _ => value,
268 };
269 known.push((16, key, transformed));
270 }
271 "sponsor" => {
272 let transformed = match &value {
273 Value::Object(o) => {
274 Value::Object(sort_object_by_key_order(o, &["type", "url"]))
275 }
276 _ => value,
277 };
278 known.push((17, key, transformed));
279 }
280 "license" => known.push((18, key, value)),
281 "qna" => known.push((19, key, value)),
282 "publisher" => known.push((20, key, value)),
283 "sideEffects" => known.push((21, key, value)),
284 "type" => known.push((22, key, value)),
285 "imports" => known.push((23, key, value)),
286 "exports" => {
287 let transformed = match &value {
288 Value::Object(o) => Value::Object(sort_exports(o)),
289 _ => value,
290 };
291 known.push((24, key, transformed));
292 }
293 "main" => known.push((25, key, value)),
294 "svelte" => known.push((26, key, value)),
295 "umd:main" => known.push((27, key, value)),
296 "jsdelivr" => known.push((28, key, value)),
297 "unpkg" => known.push((29, key, value)),
298 "module" => known.push((30, key, value)),
299 "esnext" => known.push((31, key, value)),
300 "es2020" => known.push((32, key, value)),
301 "esm2020" => known.push((33, key, value)),
302 "fesm2020" => known.push((34, key, value)),
303 "es2015" => known.push((35, key, value)),
304 "esm2015" => known.push((36, key, value)),
305 "fesm2015" => known.push((37, key, value)),
306 "es5" => known.push((38, key, value)),
307 "esm5" => known.push((39, key, value)),
308 "fesm5" => known.push((40, key, value)),
309 "source" => known.push((41, key, value)),
310 "jsnext:main" => known.push((42, key, value)),
311 "browser" => known.push((43, key, value)),
312 "umd" => known.push((44, key, value)),
313 "react-native" => known.push((45, key, value)),
314 "types" => known.push((46, key, value)),
315 "typesVersions" => known.push((47, key, value)),
316 "typings" => known.push((48, key, value)),
317 "style" => known.push((49, key, value)),
318 "example" => known.push((50, key, value)),
319 "examplestyle" => known.push((51, key, value)),
320 "assets" => known.push((52, key, value)),
321 "bin" => {
322 let transformed = match &value {
323 Value::Object(o) => Value::Object(sort_object_alphabetically(o)),
324 _ => value,
325 };
326 known.push((53, key, transformed));
327 }
328 "man" => known.push((54, key, value)),
329 "directories" => {
330 let transformed = match &value {
331 Value::Object(o) => Value::Object(sort_object_by_key_order(
332 o,
333 &["lib", "bin", "man", "doc", "example", "test"],
334 )),
335 _ => value,
336 };
337 known.push((55, key, transformed));
338 }
339 "files" => {
340 let transformed = match &value {
341 Value::Array(arr) => Value::Array(sort_array_unique(arr)),
342 _ => value,
343 };
344 known.push((56, key, transformed));
345 }
346 "workspaces" => known.push((57, key, value)),
347 "binary" => {
348 let transformed = match &value {
349 Value::Object(o) => Value::Object(sort_object_by_key_order(
350 o,
351 &["module_name", "module_path", "remote_path", "package_name", "host"],
352 )),
353 _ => value,
354 };
355 known.push((58, key, transformed));
356 }
357 "scripts" => known.push((59, key, value)),
358 "betterScripts" => known.push((60, key, value)),
359 "l10n" => known.push((61, key, value)),
360 "contributes" => known.push((62, key, value)),
361 "activationEvents" => {
362 let transformed = match &value {
363 Value::Array(arr) => Value::Array(sort_array_unique(arr)),
364 _ => value,
365 };
366 known.push((63, key, transformed));
367 }
368 "husky" => {
369 let transformed = match &value {
370 Value::Object(o) => Value::Object(sort_object_recursive(o)),
371 _ => value,
372 };
373 known.push((64, key, transformed));
374 }
375 "simple-git-hooks" => known.push((65, key, value)),
376 "pre-commit" => known.push((66, key, value)),
377 "commitlint" => {
378 let transformed = match &value {
379 Value::Object(o) => Value::Object(sort_object_recursive(o)),
380 _ => value,
381 };
382 known.push((67, key, transformed));
383 }
384 "lint-staged" => known.push((68, key, value)),
385 "nano-staged" => known.push((69, key, value)),
386 "resolutions" => {
387 let transformed = match &value {
388 Value::Object(o) => Value::Object(sort_object_alphabetically(o)),
389 _ => value,
390 };
391 known.push((70, key, transformed));
392 }
393 "overrides" => {
394 let transformed = match &value {
395 Value::Object(o) => Value::Object(sort_object_alphabetically(o)),
396 _ => value,
397 };
398 known.push((71, key, transformed));
399 }
400 "dependencies" => {
401 let transformed = match &value {
402 Value::Object(o) => Value::Object(sort_object_alphabetically(o)),
403 _ => value,
404 };
405 known.push((72, key, transformed));
406 }
407 "devDependencies" => {
408 let transformed = match &value {
409 Value::Object(o) => Value::Object(sort_object_alphabetically(o)),
410 _ => value,
411 };
412 known.push((73, key, transformed));
413 }
414 "dependenciesMeta" => known.push((74, key, value)),
415 "peerDependencies" => {
416 let transformed = match &value {
417 Value::Object(o) => Value::Object(sort_object_alphabetically(o)),
418 _ => value,
419 };
420 known.push((75, key, transformed));
421 }
422 "peerDependenciesMeta" => known.push((76, key, value)),
423 "optionalDependencies" => {
424 let transformed = match &value {
425 Value::Object(o) => Value::Object(sort_object_alphabetically(o)),
426 _ => value,
427 };
428 known.push((77, key, transformed));
429 }
430 "bundledDependencies" => {
431 let transformed = match &value {
432 Value::Array(arr) => Value::Array(sort_array_unique(arr)),
433 _ => value,
434 };
435 known.push((78, key, transformed));
436 }
437 "bundleDependencies" => {
438 let transformed = match &value {
439 Value::Array(arr) => Value::Array(sort_array_unique(arr)),
440 _ => value,
441 };
442 known.push((79, key, transformed));
443 }
444 "extensionPack" => {
445 let transformed = match &value {
446 Value::Array(arr) => Value::Array(sort_array_unique(arr)),
447 _ => value,
448 };
449 known.push((80, key, transformed));
450 }
451 "extensionDependencies" => {
452 let transformed = match &value {
453 Value::Array(arr) => Value::Array(sort_array_unique(arr)),
454 _ => value,
455 };
456 known.push((81, key, transformed));
457 }
458 "extensionKind" => {
459 let transformed = match &value {
460 Value::Array(arr) => Value::Array(sort_array_unique(arr)),
461 _ => value,
462 };
463 known.push((82, key, transformed));
464 }
465 "flat" => known.push((83, key, value)),
466 "packageManager" => known.push((84, key, value)),
467 "config" => {
468 let transformed = match &value {
469 Value::Object(o) => Value::Object(sort_object_alphabetically(o)),
470 _ => value,
471 };
472 known.push((85, key, transformed));
473 }
474 "nodemonConfig" => {
475 let transformed = match &value {
476 Value::Object(o) => Value::Object(sort_object_recursive(o)),
477 _ => value,
478 };
479 known.push((86, key, transformed));
480 }
481 "browserify" => {
482 let transformed = match &value {
483 Value::Object(o) => Value::Object(sort_object_recursive(o)),
484 _ => value,
485 };
486 known.push((87, key, transformed));
487 }
488 "babel" => {
489 let transformed = match &value {
490 Value::Object(o) => Value::Object(sort_object_recursive(o)),
491 _ => value,
492 };
493 known.push((88, key, transformed));
494 }
495 "browserslist" => known.push((89, key, value)),
496 "xo" => {
497 let transformed = match &value {
498 Value::Object(o) => Value::Object(sort_object_recursive(o)),
499 _ => value,
500 };
501 known.push((90, key, transformed));
502 }
503 "prettier" => {
504 let transformed = match &value {
505 Value::Object(o) => Value::Object(sort_object_recursive(o)),
506 _ => value,
507 };
508 known.push((91, key, transformed));
509 }
510 "eslintConfig" => {
511 let transformed = match &value {
512 Value::Object(o) => Value::Object(sort_object_recursive(o)),
513 _ => value,
514 };
515 known.push((92, key, transformed));
516 }
517 "eslintIgnore" => known.push((93, key, value)),
518 "npmpkgjsonlint" => known.push((94, key, value)),
519 "npmPackageJsonLintConfig" => known.push((95, key, value)),
520 "npmpackagejsonlint" => known.push((96, key, value)),
521 "release" => known.push((97, key, value)),
522 "remarkConfig" => {
523 let transformed = match &value {
524 Value::Object(o) => Value::Object(sort_object_recursive(o)),
525 _ => value,
526 };
527 known.push((98, key, transformed));
528 }
529 "stylelint" => {
530 let transformed = match &value {
531 Value::Object(o) => Value::Object(sort_object_recursive(o)),
532 _ => value,
533 };
534 known.push((99, key, transformed));
535 }
536 "ava" => {
537 let transformed = match &value {
538 Value::Object(o) => Value::Object(sort_object_recursive(o)),
539 _ => value,
540 };
541 known.push((100, key, transformed));
542 }
543 "jest" => {
544 let transformed = match &value {
545 Value::Object(o) => Value::Object(sort_object_recursive(o)),
546 _ => value,
547 };
548 known.push((101, key, transformed));
549 }
550 "jest-junit" => known.push((102, key, value)),
551 "jest-stare" => known.push((103, key, value)),
552 "mocha" => {
553 let transformed = match &value {
554 Value::Object(o) => Value::Object(sort_object_recursive(o)),
555 _ => value,
556 };
557 known.push((104, key, transformed));
558 }
559 "nyc" => {
560 let transformed = match &value {
561 Value::Object(o) => Value::Object(sort_object_recursive(o)),
562 _ => value,
563 };
564 known.push((105, key, transformed));
565 }
566 "c8" => {
567 let transformed = match &value {
568 Value::Object(o) => Value::Object(sort_object_recursive(o)),
569 _ => value,
570 };
571 known.push((106, key, transformed));
572 }
573 "tap" => known.push((107, key, value)),
574 "oclif" => {
575 let transformed = match &value {
576 Value::Object(o) => Value::Object(sort_object_recursive(o)),
577 _ => value,
578 };
579 known.push((108, key, transformed));
580 }
581 "engines" => {
582 let transformed = match &value {
583 Value::Object(o) => Value::Object(sort_object_alphabetically(o)),
584 _ => value,
585 };
586 known.push((109, key, transformed));
587 }
588 "engineStrict" => known.push((110, key, value)),
589 "volta" => {
590 let transformed = match &value {
591 Value::Object(o) => Value::Object(sort_object_recursive(o)),
592 _ => value,
593 };
594 known.push((111, key, transformed));
595 }
596 "languageName" => known.push((112, key, value)),
597 "os" => known.push((113, key, value)),
598 "cpu" => known.push((114, key, value)),
599 "libc" => {
600 let transformed = match &value {
601 Value::Array(arr) => Value::Array(sort_array_unique(arr)),
602 _ => value,
603 };
604 known.push((115, key, transformed));
605 }
606 "devEngines" => {
607 let transformed = match &value {
608 Value::Object(o) => Value::Object(sort_object_alphabetically(o)),
609 _ => value,
610 };
611 known.push((116, key, transformed));
612 }
613 "preferGlobal" => known.push((117, key, value)),
614 "publishConfig" => {
615 let transformed = match &value {
616 Value::Object(o) => Value::Object(sort_object_alphabetically(o)),
617 _ => value,
618 };
619 known.push((118, key, transformed));
620 }
621 "icon" => known.push((119, key, value)),
622 "badges" => known.push((120, key, value)),
623 "galleryBanner" => known.push((121, key, value)),
624 "preview" => known.push((122, key, value)),
625 "markdown" => known.push((123, key, value)),
626 "pnpm" => known.push((124, key, value)),
627 _ => {
628 if key.starts_with('_') {
630 private.push((key, value));
631 } else {
632 non_private.push((key, value));
633 }
634 }
635 }
636 }
637
638 known.sort_by_key(|(index, _, _)| *index);
640 non_private.sort_by(|(a, _), (b, _)| a.cmp(b));
641 private.sort_by(|(a, _), (b, _)| a.cmp(b));
642
643 let mut result = Map::new();
645
646 for (_index, key, value) in known {
648 result.insert(key, value);
649 }
650
651 for (key, value) in non_private {
653 result.insert(key, value);
654 }
655
656 for (key, value) in private {
658 result.insert(key, value);
659 }
660
661 result
662}