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