@tamtamchik/json-deep-sort 1.5.0 → 1.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,122 +1,222 @@
1
- # JSON Deep Sort
2
-
3
- [![Buy Me A Coffee][ico-coffee]][link-coffee]
4
- [![Latest Version on NPM][ico-version]][link-npm]
5
- [![Scrutinizer build][ico-scrutinizer-build]][link-scrutinizer]
6
- [![Scrutinizer quality][ico-scrutinizer-quality]][link-scrutinizer]
7
- [![Scrutinizer coverage][ico-scrutinizer-coverage]][link-scrutinizer]
8
- [![Software License][ico-license]](LICENSE)
9
- [![Total Downloads][ico-downloads]][link-downloads]
10
-
11
- JSON Deep Sort is a powerful and versatile TypeScript package designed for efficient sorting of JSON objects by keys. It offers:
12
-
13
- - Comprehensive handling of deeply nested objects and arrays
14
- - Flexible sorting options, including ascending and descending order
15
- - Support for both synchronous and asynchronous operations
16
- - Preservation of non-sortable objects (e.g., Date, RegExp, Function)
17
- - Lightweight implementation with zero dependencies
18
-
19
- Whether you're working with simple flat objects or complex nested structures, JSON Deep Sort provides a reliable solution for organizing your data.
20
-
21
- ## Installation
22
-
23
- Using npm:
1
+ <p align="center">
2
+ <img src="https://raw.githubusercontent.com/tamtamchik/json-deep-sort/main/docs/assets/json-deep-sort-readme-banner.png" alt="JSON Deep Sort" width="100%">
3
+ </p>
4
+
5
+ <p align="center">
6
+ <strong>Recursive JSON key sorting for JavaScript and TypeScript.</strong>
7
+ </p>
8
+
9
+ <p align="center">
10
+ <a href="https://www.npmjs.com/package/@tamtamchik/json-deep-sort"><img alt="Latest version on npm" src="https://img.shields.io/npm/v/@tamtamchik/json-deep-sort?style=flat-square"></a>
11
+ <a href="https://www.npmjs.com/package/@tamtamchik/json-deep-sort"><img alt="Monthly downloads" src="https://img.shields.io/npm/dm/@tamtamchik/json-deep-sort?style=flat-square"></a>
12
+ <a href="https://www.npmjs.com/package/@tamtamchik/json-deep-sort"><img alt="Total downloads" src="https://img.shields.io/npm/dt/@tamtamchik/json-deep-sort?style=flat-square"></a>
13
+ <a href="https://github.com/tamtamchik/json-deep-sort/actions/workflows/ci.yml"><img alt="CI" src="https://img.shields.io/github/actions/workflow/status/tamtamchik/json-deep-sort/ci.yml?branch=main&style=flat-square&label=CI"></a>
14
+ <a href="https://scrutinizer-ci.com/g/tamtamchik/json-deep-sort/"><img alt="Scrutinizer build" src="https://img.shields.io/scrutinizer/build/g/tamtamchik/json-deep-sort/main?style=flat-square"></a>
15
+ <a href="https://scrutinizer-ci.com/g/tamtamchik/json-deep-sort/"><img alt="Scrutinizer quality" src="https://img.shields.io/scrutinizer/quality/g/tamtamchik/json-deep-sort/main?style=flat-square"></a>
16
+ <a href="https://scrutinizer-ci.com/g/tamtamchik/json-deep-sort/"><img alt="Code coverage" src="https://img.shields.io/scrutinizer/coverage/g/tamtamchik/json-deep-sort/main?style=flat-square"></a>
17
+ <a href="LICENSE"><img alt="License MIT" src="https://img.shields.io/badge/license-MIT-22c55e?style=flat-square"></a>
18
+ </p>
19
+
20
+ <p align="center">
21
+ <a href="#quick-start">Quick Start</a> ·
22
+ <a href="#usage">Usage</a> ·
23
+ <a href="#development-setup">Development Setup</a> ·
24
+ <a href="#documentation">Documentation</a> ·
25
+ <a href="#security-notes">Security Notes</a> ·
26
+ <a href="#support">Support</a>
27
+ </p>
28
+
29
+ JSON Deep Sort sorts object keys throughout nested objects and arrays without
30
+ mutating the input. It supports ascending and descending order, optional sorting
31
+ of primitive arrays, ES modules and CommonJS, and includes TypeScript types.
32
+ The library has no runtime dependencies.
33
+
34
+ ## Quick Start
35
+
36
+ Install with npm:
24
37
 
25
38
  ```shell
26
39
  npm install @tamtamchik/json-deep-sort
27
40
  ```
28
41
 
29
- Using yarn:
42
+ Or with yarn:
30
43
 
31
44
  ```shell
32
45
  yarn add @tamtamchik/json-deep-sort
33
46
  ```
34
47
 
35
- ## Usage
36
-
37
- Import JSON Deep Sort in your TypeScript file:
48
+ Sort an object:
38
49
 
39
50
  ```typescript
40
51
  import { sort } from '@tamtamchik/json-deep-sort';
52
+
53
+ const data = { b: 2, a: { d: 4, c: 3 } };
54
+ console.log(sort(data));
55
+ // { a: { c: 3, d: 4 }, b: 2 }
41
56
  ```
42
57
 
43
- Here is an example of sorting a JSON object:
58
+ ## Usage
59
+
60
+ Pass an object, array, or primitive value to `sort`. It returns the result
61
+ synchronously. Object keys are sorted in ascending order by default; array
62
+ items keep their order unless primitive array sorting is enabled.
63
+
64
+ The Quick Start example uses an ES module import. For CommonJS, use `require`:
65
+
66
+ ### CommonJS
67
+
68
+ ```javascript
69
+ const { sort } = require('@tamtamchik/json-deep-sort');
70
+
71
+ console.log(sort({ b: 'b', a: 'a', c: 'c' }));
72
+ // { a: 'a', b: 'b', c: 'c' }
73
+ ```
74
+
75
+ ### Nested Objects and Arrays
76
+
77
+ Objects inside arrays are sorted recursively, while the array items keep their
78
+ positions:
44
79
 
45
80
  ```typescript
46
- // Example 1: Sorting a simple object
47
- const simpleData = { b: 'b', a: 'a', c: 'c' };
48
- console.log(sort(simpleData));
49
- // Output: { a: 'a', b: 'b', c: 'c' }
50
-
51
- // Example 2: Sorting a nested object
52
- const nestedData = {
53
- b: 'b',
54
- a: 'a',
55
- c: { d: 'd', c: 'c', a: 'a', b: 'b' },
56
- };
57
- console.log(sort(nestedData));
58
- // Output: { a: 'a', b: 'b', c: { a: 'a', b: 'b', c: 'c', d: 'd' } }
59
-
60
- // Example 3: Sorting an array of objects
61
- const arrayData = [
62
- { b: 'b', a: 'a' },
63
- { d: 'd', c: 'c' },
64
- ];
65
- console.log(sort(arrayData));
66
- // Output: [{ a: 'a', b: 'b' }, { c: 'c', d: 'd' }]
67
-
68
- // Example 4: Sorting in descending order
69
- const descendingData = { a: 'a', c: 'c', b: 'b' };
70
- console.log(sort(descendingData, false));
71
- // Output: { c: 'c', b: 'b', a: 'a' }
72
-
73
- // Example 5: Handling mixed data types
74
- const mixedData = {
81
+ const data = {
75
82
  b: [3, 1, 2],
76
- a: { z: 'z', y: 'y' },
77
- c: new Date('2023-01-01'),
83
+ a: [
84
+ { b: 'b', a: 'a' },
85
+ { d: 'd', c: 'c' },
86
+ ],
78
87
  };
79
- console.log(sort(mixedData));
80
- // Output: { a: { y: 'y', z: 'z' }, b: [3, 1, 2], c: Date('2023-01-01') }
81
88
 
82
- // Example 6: Sorting arrays of primitives
83
- const primitiveArrayData = ['b', 'a', 'c'];
84
- console.log(sort(primitiveArrayData, true, true));
85
- // Output: ['a', 'b', 'c']
89
+ console.log(sort(data));
90
+ // { a: [{ a: 'a', b: 'b' }, { c: 'c', d: 'd' }], b: [3, 1, 2] }
91
+ ```
92
+
93
+ ### Descending Order
94
+
95
+ Set the second argument to `false` to sort keys in descending order at each level:
96
+
97
+ ```typescript
98
+ console.log(sort({ a: 'a', c: 'c', b: 'b' }, false));
99
+ // { c: 'c', b: 'b', a: 'a' }
86
100
  ```
87
101
 
88
- ## API Reference
102
+ ### Primitive Arrays
89
103
 
90
- ### `sort(data, ascending?, sortPrimitiveArrays?)`
104
+ Set the third argument to `true` to sort arrays of strings, numbers, or booleans,
105
+ including arrays nested inside objects:
91
106
 
92
- Sorts a JSON object or array by keys in a specified order.
107
+ ```typescript
108
+ console.log(sort(['b', 'a', 'c'], true, true));
109
+ // ['a', 'b', 'c']
93
110
 
94
- **Parameters:**
111
+ console.log(sort([3, 1, 2], false, true));
112
+ // [3, 2, 1]
95
113
 
96
- - `data` (required): The data to sort. Can be an object, array, or primitive value.
97
- - `ascending` (optional): Boolean flag to determine sort order. Defaults to `true` (ascending).
98
- - `sortPrimitiveArrays` (optional): Boolean flag to enable sorting of arrays containing primitive values (strings, numbers, booleans). Defaults to `false` to maintain backward compatibility.
114
+ console.log(sort({ values: [3, 1, 2] }, true, true));
115
+ // { values: [1, 2, 3] }
116
+ ```
99
117
 
100
- **Returns:** The sorted data with the same structure and type as the input.
118
+ Numbers use numeric order, strings use `localeCompare`, and booleans sort as
119
+ `false` before `true` in ascending order. Arrays containing `null`, `undefined`,
120
+ or objects retain their item order. Arrays mixing strings, numbers, and booleans
121
+ also keep their original order.
101
122
 
102
- **Note:** When `sortPrimitiveArrays` is `false` (default), arrays of primitives are not modified, preserving the original behavior. When `true`, primitive arrays are sorted according to the `ascending` parameter.
123
+ ### Preserved Values
124
+
125
+ Primitive values pass through unchanged. Values such as `Date`, `RegExp`,
126
+ functions, `Map`, and `Set` retain their original references:
127
+
128
+ ```typescript
129
+ const date = new Date('2023-01-01');
130
+ const result = sort({ b: date, a: { z: 'z', y: 'y' } });
131
+
132
+ console.log(result.a);
133
+ // { y: 'y', z: 'z' }
134
+ console.log(result.b === date);
135
+ // true
136
+ ```
137
+
138
+ ### API Reference
139
+
140
+ #### `sort<T>(data: T, ascending = true, sortPrimitiveArrays = false): T`
141
+
142
+ | Parameter | Default | Description |
143
+ | --------------------- | -------- | ----------------------------------------------------------- |
144
+ | `data` | Required | Object, array, or primitive value to sort. |
145
+ | `ascending` | `true` | Sort keys and eligible primitive arrays in ascending order. |
146
+ | `sortPrimitiveArrays` | `false` | Enable sorting of arrays containing primitive values. |
147
+
148
+ The return type is inferred from the input. Traversed objects and arrays are
149
+ rebuilt; preserved values keep their references. JavaScript enumeration rules
150
+ keep integer-index keys in numeric order, even when descending order is requested.
151
+ String key ordering uses `localeCompare` and can depend on the runtime locale.
152
+
153
+ ## Development Setup
154
+
155
+ Use Node.js 22 (22.18 or later), Node.js 24 (24.11 or later), or Node.js 26.
156
+ The `.nvmrc` file selects Node.js 24; npm enforces these development versions
157
+ through `devEngines`. CI tests Node.js 22, 24, and 26. These requirements apply
158
+ to development tooling; the published library adds no Node.js engine restriction.
159
+
160
+ Install dependencies:
161
+
162
+ ```shell
163
+ npm ci
164
+ ```
165
+
166
+ Run the checks used in CI:
167
+
168
+ ```shell
169
+ npm run check
170
+ npm run typecheck
171
+ npm run build
172
+ npm test
173
+ npm run test:package
174
+ ```
175
+
176
+ Useful focused commands:
177
+
178
+ ```shell
179
+ npm run fix # Format files and apply safe lint fixes with Biome.
180
+ npm run dev # Rebuild when source files change.
181
+ npm run coverage # Run unit tests and generate coverage reports.
182
+ ```
183
+
184
+ Biome checks TypeScript, JavaScript, and JSON. Markdown and YAML are reviewed
185
+ manually. Coverage runs deterministic unit tests; `test:package` installs a packed
186
+ archive in an isolated consumer and checks CommonJS, ES modules, TypeScript types,
187
+ and package contents.
188
+
189
+ ## Documentation
190
+
191
+ - [npm package](https://www.npmjs.com/package/@tamtamchik/json-deep-sort): published versions and installation details.
192
+ - [API reference](#api-reference): arguments, defaults, and return values.
193
+ - [Source](src/index.ts): public function and exported TypeScript types.
194
+ - [Unit tests](test/sort.test.ts): nested objects, arrays, sort order, and preserved values.
195
+ - [Package tests](test/package.test.mjs): packed files, imports, and consumer types.
196
+ - [Security policy](SECURITY.md): vulnerability reporting.
197
+ - [CI](https://github.com/tamtamchik/json-deep-sort/actions/workflows/ci.yml): formatting, lint, build, and test results.
198
+
199
+ ## Security Notes
200
+
201
+ Use acyclic JSON data. Circular references are unsupported, and very deep inputs
202
+ can exceed the JavaScript call stack. Sorting keys does not validate or sanitize
203
+ input, and locale-dependent ordering is unsuitable for cryptographic JSON
204
+ canonicalization.
205
+
206
+ Report vulnerabilities by emailing [yuri.tam.tkachenko@gmail.com](mailto:yuri.tam.tkachenko@gmail.com).
207
+ See [SECURITY.md](SECURITY.md) for the reporting policy.
103
208
 
104
209
  ## Contributing
105
210
 
106
- Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.
211
+ Pull requests are welcome. For major changes, [open an issue](https://github.com/tamtamchik/json-deep-sort/issues)
212
+ first to discuss the proposal.
213
+
214
+ ## Support
215
+
216
+ <p>
217
+ <a href="https://www.buymeacoffee.com/tamtamchik"><img alt="Buy me a coffee" src="https://img.shields.io/badge/Buy%20Me%20A-Coffee-6F4E37?style=flat-square&logo=buymeacoffee&logoColor=white"></a>
218
+ </p>
107
219
 
108
220
  ## License
109
221
 
110
- JSON Deep Sort is [MIT licensed](./LICENSE).
111
-
112
- [ico-coffee]: https://img.shields.io/badge/Buy%20Me%20A-Coffee-%236F4E37.svg?style=flat-square
113
- [ico-version]: https://img.shields.io/npm/v/@tamtamchik/json-deep-sort.svg?style=flat-square
114
- [ico-license]: https://img.shields.io/npm/l/@tamtamchik/json-deep-sort.svg?style=flat-square
115
- [ico-downloads]: https://img.shields.io/npm/dt/@tamtamchik/json-deep-sort.svg?style=flat-square
116
- [ico-scrutinizer-build]: https://img.shields.io/scrutinizer/build/g/tamtamchik/json-deep-sort/main.svg?style=flat-square
117
- [ico-scrutinizer-quality]: https://img.shields.io/scrutinizer/quality/g/tamtamchik/json-deep-sort/main.svg?style=flat-square
118
- [ico-scrutinizer-coverage]: https://img.shields.io/scrutinizer/coverage/g/tamtamchik/json-deep-sort/main.svg?style=flat-square
119
- [link-coffee]: https://www.buymeacoffee.com/tamtamchik
120
- [link-npm]: https://www.npmjs.com/package/@tamtamchik/json-deep-sort
121
- [link-downloads]: https://www.npmjs.com/package/@tamtamchik/json-deep-sort
122
- [link-scrutinizer]: https://scrutinizer-ci.com/g/tamtamchik/json-deep-sort/
222
+ [MIT](LICENSE).
@@ -0,0 +1,13 @@
1
+ //#region src/types.d.ts
2
+ type ObjectType = Record<string | symbol, unknown>;
3
+ type SortedEntry = [string | symbol, unknown];
4
+ type NonSortableType = Date | RegExp | (() => unknown) | ((...args: unknown[]) => unknown) | Error | Map<unknown, unknown> | Set<unknown> | WeakMap<object, unknown> | WeakSet<object> | Promise<unknown>;
5
+ type SortOptions = {
6
+ ascending: boolean;
7
+ sortPrimitiveArrays: boolean;
8
+ };
9
+ //#endregion
10
+ //#region src/index.d.ts
11
+ export declare function sort<T>(data: T, ascending?: boolean, sortPrimitiveArrays?: boolean): T;
12
+ //#endregion
13
+ export type { NonSortableType, ObjectType, SortOptions, SortedEntry };
@@ -0,0 +1,13 @@
1
+ //#region src/types.d.ts
2
+ type ObjectType = Record<string | symbol, unknown>;
3
+ type SortedEntry = [string | symbol, unknown];
4
+ type NonSortableType = Date | RegExp | (() => unknown) | ((...args: unknown[]) => unknown) | Error | Map<unknown, unknown> | Set<unknown> | WeakMap<object, unknown> | WeakSet<object> | Promise<unknown>;
5
+ type SortOptions = {
6
+ ascending: boolean;
7
+ sortPrimitiveArrays: boolean;
8
+ };
9
+ //#endregion
10
+ //#region src/index.d.ts
11
+ export declare function sort<T>(data: T, ascending?: boolean, sortPrimitiveArrays?: boolean): T;
12
+ //#endregion
13
+ export type { NonSortableType, ObjectType, SortOptions, SortedEntry };
package/dist/index.js CHANGED
@@ -1,181 +1,126 @@
1
- "use strict";
2
- var __defProp = Object.defineProperty;
3
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
- var __getOwnPropNames = Object.getOwnPropertyNames;
5
- var __hasOwnProp = Object.prototype.hasOwnProperty;
6
- var __export = (target, all) => {
7
- for (var name in all)
8
- __defProp(target, name, { get: all[name], enumerable: true });
9
- };
10
- var __copyProps = (to, from, except, desc) => {
11
- if (from && typeof from === "object" || typeof from === "function") {
12
- for (let key of __getOwnPropNames(from))
13
- if (!__hasOwnProp.call(to, key) && key !== except)
14
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
- }
16
- return to;
17
- };
18
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
-
20
- // src/index.ts
21
- var index_exports = {};
22
- __export(index_exports, {
23
- sort: () => sort
24
- });
25
- module.exports = __toCommonJS(index_exports);
26
-
27
- // src/types.ts
28
- var NULLISH_VALUES = [null, void 0];
29
- var SORTABLE_PRIMITIVE_TYPES = [
30
- "string",
31
- "number",
32
- "boolean"
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ //#region src/types.ts
3
+ const NULLISH_VALUES = [null, void 0];
4
+ const SORTABLE_PRIMITIVE_TYPES = [
5
+ "string",
6
+ "number",
7
+ "boolean"
33
8
  ];
34
-
35
- // src/type-guards.ts
9
+ //#endregion
10
+ //#region src/type-guards.ts
36
11
  function isNullish(value) {
37
- return NULLISH_VALUES.includes(value);
12
+ return NULLISH_VALUES.includes(value);
38
13
  }
39
14
  function hasNullishValues(array) {
40
- return array.some(isNullish);
15
+ return array.some(isNullish);
41
16
  }
42
17
  function isSortablePrimitive(value) {
43
- return SORTABLE_PRIMITIVE_TYPES.includes(
44
- typeof value
45
- );
18
+ return SORTABLE_PRIMITIVE_TYPES.includes(typeof value);
46
19
  }
47
20
  function isPrimitive(data) {
48
- return isSortablePrimitive(data) || isNullish(data);
21
+ return isSortablePrimitive(data) || isNullish(data);
49
22
  }
50
23
  function isObject(data) {
51
- return typeof data === "object" && data !== null;
24
+ return typeof data === "object" && data !== null;
52
25
  }
53
26
  function isNonSortableObject(obj) {
54
- const nonSortableTypes = [
55
- Date,
56
- RegExp,
57
- Function,
58
- Error,
59
- Map,
60
- Set,
61
- WeakMap,
62
- WeakSet,
63
- Promise
64
- ];
65
- return nonSortableTypes.some((type) => obj instanceof type) || Symbol.iterator in Object(obj);
66
- }
67
-
68
- // src/comparators.ts
27
+ return [
28
+ Date,
29
+ RegExp,
30
+ Function,
31
+ Error,
32
+ Map,
33
+ Set,
34
+ WeakMap,
35
+ WeakSet,
36
+ Promise
37
+ ].some((type) => obj instanceof type) || Symbol.iterator in Object(obj);
38
+ }
39
+ //#endregion
40
+ //#region src/comparators.ts
69
41
  function compareSortablePrimitives(a, b, ascending) {
70
- if (typeof a === "string" && typeof b === "string") {
71
- return ascending ? a.localeCompare(b) : b.localeCompare(a);
72
- }
73
- if (typeof a === "number" && typeof b === "number") {
74
- return compareNumbers(a, b, ascending);
75
- }
76
- if (typeof a === "boolean" && typeof b === "boolean") {
77
- return compareBooleans(a, b, ascending);
78
- }
79
- return 0;
42
+ if (typeof a === "string" && typeof b === "string") return ascending ? a.localeCompare(b) : b.localeCompare(a);
43
+ if (typeof a === "number" && typeof b === "number") return compareNumbers(a, b, ascending);
44
+ if (typeof a === "boolean" && typeof b === "boolean") return compareBooleans(a, b, ascending);
45
+ return 0;
80
46
  }
81
47
  function compareNumbers(a, b, ascending) {
82
- if (Number.isNaN(a) && Number.isNaN(b)) return 0;
83
- if (Number.isNaN(a)) return 1;
84
- if (Number.isNaN(b)) return -1;
85
- return ascending ? a - b : b - a;
48
+ if (Number.isNaN(a) && Number.isNaN(b)) return 0;
49
+ if (Number.isNaN(a)) return 1;
50
+ if (Number.isNaN(b)) return -1;
51
+ return ascending ? a - b : b - a;
86
52
  }
87
53
  function compareBooleans(a, b, ascending) {
88
- if (a === b) return 0;
89
- if (a) return ascending ? 1 : -1;
90
- return ascending ? -1 : 1;
54
+ if (a === b) return 0;
55
+ if (a) return ascending ? 1 : -1;
56
+ return ascending ? -1 : 1;
91
57
  }
92
58
  function compareObjectKeys(keyA, keyB, ascending) {
93
- if (typeof keyA === "symbol" && typeof keyB === "symbol") return 0;
94
- if (typeof keyA === "symbol") return 1;
95
- if (typeof keyB === "symbol") return -1;
96
- const stringA = keyA;
97
- const stringB = keyB;
98
- return ascending ? stringA.localeCompare(stringB) : stringB.localeCompare(stringA);
99
- }
100
-
101
- // src/array-sorter.ts
59
+ if (typeof keyA === "symbol" && typeof keyB === "symbol") return 0;
60
+ if (typeof keyA === "symbol") return 1;
61
+ if (typeof keyB === "symbol") return -1;
62
+ const stringA = keyA;
63
+ const stringB = keyB;
64
+ return ascending ? stringA.localeCompare(stringB) : stringB.localeCompare(stringA);
65
+ }
66
+ //#endregion
67
+ //#region src/array-sorter.ts
102
68
  function sortArray(array, options) {
103
- if (shouldSortPrimitiveArray(array, options.sortPrimitiveArrays)) {
104
- return sortPrimitiveArray(array, options.ascending);
105
- }
106
- return array.map((item) => sortRecursively(item, options));
69
+ if (shouldSortPrimitiveArray(array, options.sortPrimitiveArrays)) return sortPrimitiveArray(array, options.ascending);
70
+ return array.map((item) => sortRecursively(item, options));
107
71
  }
108
72
  function shouldSortPrimitiveArray(array, sortPrimitiveArrays) {
109
- return sortPrimitiveArrays && canSortPrimitiveArray(array);
73
+ return sortPrimitiveArrays && canSortPrimitiveArray(array);
110
74
  }
111
75
  function canSortPrimitiveArray(array) {
112
- return allItemsAreSortablePrimitives(array) && allItemsHaveSameSortableType(array);
76
+ return allItemsAreSortablePrimitives(array) && allItemsHaveSameSortableType(array);
113
77
  }
114
78
  function allItemsAreSortablePrimitives(array) {
115
- return array.every(isSortablePrimitive);
79
+ return array.every(isSortablePrimitive);
116
80
  }
117
81
  function sortPrimitiveArray(array, ascending) {
118
- return [...array].sort((a, b) => compareSortablePrimitives(a, b, ascending));
82
+ return [...array].sort((a, b) => compareSortablePrimitives(a, b, ascending));
119
83
  }
120
84
  function allItemsHaveSameSortableType(array) {
121
- if (array.length === 0) return true;
122
- if (hasNullishValues(array)) {
123
- return false;
124
- }
125
- const firstItem = array[0];
126
- if (!isSortablePrimitive(firstItem)) return false;
127
- const expectedType = typeof firstItem;
128
- const allSameType = array.every((item) => typeof item === expectedType);
129
- if (allSameType) return true;
130
- return array.every(isSortablePrimitive);
131
- }
132
-
133
- // src/object-sorter.ts
85
+ if (array.length === 0) return true;
86
+ if (hasNullishValues(array)) return false;
87
+ const firstItem = array[0];
88
+ if (!isSortablePrimitive(firstItem)) return false;
89
+ const expectedType = typeof firstItem;
90
+ return array.every((item) => typeof item === expectedType);
91
+ }
92
+ //#endregion
93
+ //#region src/object-sorter.ts
134
94
  function sortObject(obj, options) {
135
- const entries = collectObjectEntries(obj);
136
- const sortedEntries = sortObjectEntries(entries, options.ascending);
137
- return createSortedObject(sortedEntries, options);
95
+ return createSortedObject(sortObjectEntries(collectObjectEntries(obj), options.ascending), options);
138
96
  }
139
97
  function collectObjectEntries(obj) {
140
- const stringEntries = Object.entries(obj);
141
- const symbolEntries = Object.getOwnPropertySymbols(obj).map(
142
- (symbol) => [symbol, obj[symbol]]
143
- );
144
- return [...stringEntries, ...symbolEntries];
98
+ const stringEntries = Object.entries(obj);
99
+ const symbolEntries = Object.getOwnPropertySymbols(obj).map((symbol) => [symbol, obj[symbol]]);
100
+ return [...stringEntries, ...symbolEntries];
145
101
  }
146
102
  function sortObjectEntries(entries, ascending) {
147
- return entries.sort(
148
- ([keyA], [keyB]) => compareObjectKeys(keyA, keyB, ascending)
149
- );
103
+ return entries.sort(([keyA], [keyB]) => compareObjectKeys(keyA, keyB, ascending));
150
104
  }
151
105
  function createSortedObject(entries, options) {
152
- const sortedEntries = entries.map(([key, value]) => [
153
- key,
154
- sortRecursively(value, options)
155
- ]);
156
- return Object.fromEntries(sortedEntries);
157
- }
158
-
159
- // src/core.ts
106
+ const sortedEntries = entries.map(([key, value]) => [key, sortRecursively(value, options)]);
107
+ return Object.fromEntries(sortedEntries);
108
+ }
109
+ //#endregion
110
+ //#region src/core.ts
160
111
  function sortRecursively(data, options) {
161
- if (Array.isArray(data)) {
162
- return sortArray(data, options);
163
- }
164
- if (isPrimitive(data) || isNonSortableObject(data)) {
165
- return data;
166
- }
167
- if (isObject(data)) {
168
- return sortObject(data, options);
169
- }
170
- return data;
171
- }
172
-
173
- // src/index.ts
112
+ if (Array.isArray(data)) return sortArray(data, options);
113
+ if (isPrimitive(data) || isNonSortableObject(data)) return data;
114
+ if (isObject(data)) return sortObject(data, options);
115
+ return data;
116
+ }
117
+ //#endregion
118
+ //#region src/index.ts
174
119
  function sort(data, ascending = true, sortPrimitiveArrays = false) {
175
- const options = { ascending, sortPrimitiveArrays };
176
- return sortRecursively(data, options);
120
+ return sortRecursively(data, {
121
+ ascending,
122
+ sortPrimitiveArrays
123
+ });
177
124
  }
178
- // Annotate the CommonJS export names for ESM import in node:
179
- 0 && (module.exports = {
180
- sort
181
- });
125
+ //#endregion
126
+ exports.sort = sort;
package/dist/index.mjs CHANGED
@@ -1,154 +1,125 @@
1
- // src/types.ts
2
- var NULLISH_VALUES = [null, void 0];
3
- var SORTABLE_PRIMITIVE_TYPES = [
4
- "string",
5
- "number",
6
- "boolean"
1
+ //#region src/types.ts
2
+ const NULLISH_VALUES = [null, void 0];
3
+ const SORTABLE_PRIMITIVE_TYPES = [
4
+ "string",
5
+ "number",
6
+ "boolean"
7
7
  ];
8
-
9
- // src/type-guards.ts
8
+ //#endregion
9
+ //#region src/type-guards.ts
10
10
  function isNullish(value) {
11
- return NULLISH_VALUES.includes(value);
11
+ return NULLISH_VALUES.includes(value);
12
12
  }
13
13
  function hasNullishValues(array) {
14
- return array.some(isNullish);
14
+ return array.some(isNullish);
15
15
  }
16
16
  function isSortablePrimitive(value) {
17
- return SORTABLE_PRIMITIVE_TYPES.includes(
18
- typeof value
19
- );
17
+ return SORTABLE_PRIMITIVE_TYPES.includes(typeof value);
20
18
  }
21
19
  function isPrimitive(data) {
22
- return isSortablePrimitive(data) || isNullish(data);
20
+ return isSortablePrimitive(data) || isNullish(data);
23
21
  }
24
22
  function isObject(data) {
25
- return typeof data === "object" && data !== null;
23
+ return typeof data === "object" && data !== null;
26
24
  }
27
25
  function isNonSortableObject(obj) {
28
- const nonSortableTypes = [
29
- Date,
30
- RegExp,
31
- Function,
32
- Error,
33
- Map,
34
- Set,
35
- WeakMap,
36
- WeakSet,
37
- Promise
38
- ];
39
- return nonSortableTypes.some((type) => obj instanceof type) || Symbol.iterator in Object(obj);
40
- }
41
-
42
- // src/comparators.ts
26
+ return [
27
+ Date,
28
+ RegExp,
29
+ Function,
30
+ Error,
31
+ Map,
32
+ Set,
33
+ WeakMap,
34
+ WeakSet,
35
+ Promise
36
+ ].some((type) => obj instanceof type) || Symbol.iterator in Object(obj);
37
+ }
38
+ //#endregion
39
+ //#region src/comparators.ts
43
40
  function compareSortablePrimitives(a, b, ascending) {
44
- if (typeof a === "string" && typeof b === "string") {
45
- return ascending ? a.localeCompare(b) : b.localeCompare(a);
46
- }
47
- if (typeof a === "number" && typeof b === "number") {
48
- return compareNumbers(a, b, ascending);
49
- }
50
- if (typeof a === "boolean" && typeof b === "boolean") {
51
- return compareBooleans(a, b, ascending);
52
- }
53
- return 0;
41
+ if (typeof a === "string" && typeof b === "string") return ascending ? a.localeCompare(b) : b.localeCompare(a);
42
+ if (typeof a === "number" && typeof b === "number") return compareNumbers(a, b, ascending);
43
+ if (typeof a === "boolean" && typeof b === "boolean") return compareBooleans(a, b, ascending);
44
+ return 0;
54
45
  }
55
46
  function compareNumbers(a, b, ascending) {
56
- if (Number.isNaN(a) && Number.isNaN(b)) return 0;
57
- if (Number.isNaN(a)) return 1;
58
- if (Number.isNaN(b)) return -1;
59
- return ascending ? a - b : b - a;
47
+ if (Number.isNaN(a) && Number.isNaN(b)) return 0;
48
+ if (Number.isNaN(a)) return 1;
49
+ if (Number.isNaN(b)) return -1;
50
+ return ascending ? a - b : b - a;
60
51
  }
61
52
  function compareBooleans(a, b, ascending) {
62
- if (a === b) return 0;
63
- if (a) return ascending ? 1 : -1;
64
- return ascending ? -1 : 1;
53
+ if (a === b) return 0;
54
+ if (a) return ascending ? 1 : -1;
55
+ return ascending ? -1 : 1;
65
56
  }
66
57
  function compareObjectKeys(keyA, keyB, ascending) {
67
- if (typeof keyA === "symbol" && typeof keyB === "symbol") return 0;
68
- if (typeof keyA === "symbol") return 1;
69
- if (typeof keyB === "symbol") return -1;
70
- const stringA = keyA;
71
- const stringB = keyB;
72
- return ascending ? stringA.localeCompare(stringB) : stringB.localeCompare(stringA);
73
- }
74
-
75
- // src/array-sorter.ts
58
+ if (typeof keyA === "symbol" && typeof keyB === "symbol") return 0;
59
+ if (typeof keyA === "symbol") return 1;
60
+ if (typeof keyB === "symbol") return -1;
61
+ const stringA = keyA;
62
+ const stringB = keyB;
63
+ return ascending ? stringA.localeCompare(stringB) : stringB.localeCompare(stringA);
64
+ }
65
+ //#endregion
66
+ //#region src/array-sorter.ts
76
67
  function sortArray(array, options) {
77
- if (shouldSortPrimitiveArray(array, options.sortPrimitiveArrays)) {
78
- return sortPrimitiveArray(array, options.ascending);
79
- }
80
- return array.map((item) => sortRecursively(item, options));
68
+ if (shouldSortPrimitiveArray(array, options.sortPrimitiveArrays)) return sortPrimitiveArray(array, options.ascending);
69
+ return array.map((item) => sortRecursively(item, options));
81
70
  }
82
71
  function shouldSortPrimitiveArray(array, sortPrimitiveArrays) {
83
- return sortPrimitiveArrays && canSortPrimitiveArray(array);
72
+ return sortPrimitiveArrays && canSortPrimitiveArray(array);
84
73
  }
85
74
  function canSortPrimitiveArray(array) {
86
- return allItemsAreSortablePrimitives(array) && allItemsHaveSameSortableType(array);
75
+ return allItemsAreSortablePrimitives(array) && allItemsHaveSameSortableType(array);
87
76
  }
88
77
  function allItemsAreSortablePrimitives(array) {
89
- return array.every(isSortablePrimitive);
78
+ return array.every(isSortablePrimitive);
90
79
  }
91
80
  function sortPrimitiveArray(array, ascending) {
92
- return [...array].sort((a, b) => compareSortablePrimitives(a, b, ascending));
81
+ return [...array].sort((a, b) => compareSortablePrimitives(a, b, ascending));
93
82
  }
94
83
  function allItemsHaveSameSortableType(array) {
95
- if (array.length === 0) return true;
96
- if (hasNullishValues(array)) {
97
- return false;
98
- }
99
- const firstItem = array[0];
100
- if (!isSortablePrimitive(firstItem)) return false;
101
- const expectedType = typeof firstItem;
102
- const allSameType = array.every((item) => typeof item === expectedType);
103
- if (allSameType) return true;
104
- return array.every(isSortablePrimitive);
105
- }
106
-
107
- // src/object-sorter.ts
84
+ if (array.length === 0) return true;
85
+ if (hasNullishValues(array)) return false;
86
+ const firstItem = array[0];
87
+ if (!isSortablePrimitive(firstItem)) return false;
88
+ const expectedType = typeof firstItem;
89
+ return array.every((item) => typeof item === expectedType);
90
+ }
91
+ //#endregion
92
+ //#region src/object-sorter.ts
108
93
  function sortObject(obj, options) {
109
- const entries = collectObjectEntries(obj);
110
- const sortedEntries = sortObjectEntries(entries, options.ascending);
111
- return createSortedObject(sortedEntries, options);
94
+ return createSortedObject(sortObjectEntries(collectObjectEntries(obj), options.ascending), options);
112
95
  }
113
96
  function collectObjectEntries(obj) {
114
- const stringEntries = Object.entries(obj);
115
- const symbolEntries = Object.getOwnPropertySymbols(obj).map(
116
- (symbol) => [symbol, obj[symbol]]
117
- );
118
- return [...stringEntries, ...symbolEntries];
97
+ const stringEntries = Object.entries(obj);
98
+ const symbolEntries = Object.getOwnPropertySymbols(obj).map((symbol) => [symbol, obj[symbol]]);
99
+ return [...stringEntries, ...symbolEntries];
119
100
  }
120
101
  function sortObjectEntries(entries, ascending) {
121
- return entries.sort(
122
- ([keyA], [keyB]) => compareObjectKeys(keyA, keyB, ascending)
123
- );
102
+ return entries.sort(([keyA], [keyB]) => compareObjectKeys(keyA, keyB, ascending));
124
103
  }
125
104
  function createSortedObject(entries, options) {
126
- const sortedEntries = entries.map(([key, value]) => [
127
- key,
128
- sortRecursively(value, options)
129
- ]);
130
- return Object.fromEntries(sortedEntries);
131
- }
132
-
133
- // src/core.ts
105
+ const sortedEntries = entries.map(([key, value]) => [key, sortRecursively(value, options)]);
106
+ return Object.fromEntries(sortedEntries);
107
+ }
108
+ //#endregion
109
+ //#region src/core.ts
134
110
  function sortRecursively(data, options) {
135
- if (Array.isArray(data)) {
136
- return sortArray(data, options);
137
- }
138
- if (isPrimitive(data) || isNonSortableObject(data)) {
139
- return data;
140
- }
141
- if (isObject(data)) {
142
- return sortObject(data, options);
143
- }
144
- return data;
145
- }
146
-
147
- // src/index.ts
111
+ if (Array.isArray(data)) return sortArray(data, options);
112
+ if (isPrimitive(data) || isNonSortableObject(data)) return data;
113
+ if (isObject(data)) return sortObject(data, options);
114
+ return data;
115
+ }
116
+ //#endregion
117
+ //#region src/index.ts
148
118
  function sort(data, ascending = true, sortPrimitiveArrays = false) {
149
- const options = { ascending, sortPrimitiveArrays };
150
- return sortRecursively(data, options);
119
+ return sortRecursively(data, {
120
+ ascending,
121
+ sortPrimitiveArrays
122
+ });
151
123
  }
152
- export {
153
- sort
154
- };
124
+ //#endregion
125
+ export { sort };
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@tamtamchik/json-deep-sort",
3
- "version": "1.5.0",
3
+ "version": "1.5.1",
4
4
  "description": "A comprehensive utility for sorting JSON objects by keys.",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
7
- "types": "./src/index.ts",
7
+ "types": "./dist/index.d.ts",
8
8
  "author": "Yuri Tkachenko <yuri.tam.tkachenko@gmail.com>",
9
9
  "license": "MIT",
10
10
  "homepage": "https://github.com/tamtamchik/json-deep-sort#readme",
@@ -32,35 +32,43 @@
32
32
  "json-tools"
33
33
  ],
34
34
  "devDependencies": {
35
- "@eslint/js": "^10.0.1",
36
- "@types/node": "^25.6.0",
37
- "@typescript-eslint/eslint-plugin": "^8.39.0",
38
- "@typescript-eslint/parser": "^8.39.0",
39
- "c8": "^11.0.0",
40
- "eslint": "^10.2.0",
41
- "eslint-config-prettier": "^10.1.8",
42
- "eslint-plugin-prettier": "^5.5.4",
43
- "jiti": "^2.5.1",
44
- "prettier": "^3.6.2",
45
- "tsup": "^8.3.0",
46
- "tsx": "^4.21.0",
47
- "typescript": "^6.0.2"
35
+ "@biomejs/biome": "2.5.15",
36
+ "@types/node": "^26.6.4",
37
+ "c8": "^12.0.0",
38
+ "tsdown": "^0.23.0",
39
+ "tsx": "^4.23.15",
40
+ "typescript": "~7.0.2"
48
41
  },
49
42
  "exports": {
50
43
  ".": {
51
- "types": "./src/index.ts",
44
+ "types": "./dist/index.d.ts",
52
45
  "require": "./dist/index.js",
53
46
  "import": "./dist/index.mjs"
54
47
  }
55
48
  },
56
49
  "scripts": {
57
- "build": "tsup src/index.ts --format cjs,esm --clean",
58
- "dev": "npm run build -- --watch src",
59
- "prepublishOnly": "npm run build",
50
+ "check": "biome check .",
51
+ "fix": "biome check --write .",
52
+ "typecheck": "tsc --noEmit",
53
+ "build": "tsdown",
54
+ "dev": "tsdown --watch src",
55
+ "prepublishOnly": "npm run check && npm run typecheck && npm run build && npm test && npm run test:package",
60
56
  "test": "node --import tsx --test test/*.test.ts",
61
57
  "coverage": "c8 --reporter=text --reporter=lcov node --import tsx --test test/*.test.ts",
62
- "format": "prettier --write .",
63
- "lint": "eslint . --ext .ts,.js",
64
- "lint:fix": "eslint . --ext .ts,.js --fix"
58
+ "test:package": "node test/package.test.mjs"
59
+ },
60
+ "files": [
61
+ "dist"
62
+ ],
63
+ "devEngines": {
64
+ "runtime": {
65
+ "name": "node",
66
+ "version": "^22.18.0 || ^24.11.0 || ^26.0.0",
67
+ "onFail": "error"
68
+ },
69
+ "packageManager": {
70
+ "name": "npm",
71
+ "onFail": "error"
72
+ }
65
73
  }
66
74
  }
@@ -1,61 +0,0 @@
1
- import type { SortOptions } from './types';
2
- import { isSortablePrimitive, hasNullishValues } from './type-guards';
3
- import { compareSortablePrimitives } from './comparators';
4
-
5
- export function sortArray<T>(array: T[], options: SortOptions): T[] {
6
- if (shouldSortPrimitiveArray(array, options.sortPrimitiveArrays)) {
7
- return sortPrimitiveArray(array, options.ascending) as T[];
8
- }
9
-
10
- return array.map((item) => sortRecursively(item, options));
11
- }
12
-
13
- function shouldSortPrimitiveArray(
14
- array: unknown[],
15
- sortPrimitiveArrays: boolean
16
- ): boolean {
17
- return sortPrimitiveArrays && canSortPrimitiveArray(array);
18
- }
19
-
20
- function canSortPrimitiveArray(array: unknown[]): boolean {
21
- return (
22
- allItemsAreSortablePrimitives(array) && allItemsHaveSameSortableType(array)
23
- );
24
- }
25
-
26
- function allItemsAreSortablePrimitives(array: unknown[]): boolean {
27
- return array.every(isSortablePrimitive);
28
- }
29
-
30
- function sortPrimitiveArray(array: unknown[], ascending: boolean): unknown[] {
31
- // The array has already been validated as sortable in canSortPrimitiveArray
32
- // No need to check allItemsHaveSameSortableType again
33
- return [...array].sort((a, b) => compareSortablePrimitives(a, b, ascending));
34
- }
35
-
36
- function allItemsHaveSameSortableType(array: unknown[]): boolean {
37
- if (array.length === 0) return true;
38
-
39
- // Don't sort arrays that contain null or undefined values
40
- // as they represent absence of value and don't have a natural ordering
41
- if (hasNullishValues(array)) {
42
- return false;
43
- }
44
-
45
- // For arrays with only sortable primitives, check if they have the same type
46
- const firstItem = array[0];
47
- if (!isSortablePrimitive(firstItem)) return false;
48
-
49
- const expectedType = typeof firstItem;
50
- const allSameType = array.every((item) => typeof item === expectedType);
51
-
52
- // If all items are the same type, we can sort them normally
53
- if (allSameType) return true;
54
-
55
- // If we have mixed primitive types, we can still "sort" them
56
- // (the comparison function will return 0 for different types, maintaining order)
57
- // This allows us to cover the fallback case in compareSortablePrimitives
58
- return array.every(isSortablePrimitive);
59
- }
60
-
61
- import { sortRecursively } from './core';
@@ -1,59 +0,0 @@
1
- // Comparison functions for different data types
2
- export function compareSortablePrimitives(
3
- a: unknown,
4
- b: unknown,
5
- ascending: boolean
6
- ): number {
7
- if (typeof a === 'string' && typeof b === 'string') {
8
- return ascending ? a.localeCompare(b) : b.localeCompare(a);
9
- }
10
-
11
- if (typeof a === 'number' && typeof b === 'number') {
12
- return compareNumbers(a, b, ascending);
13
- }
14
-
15
- if (typeof a === 'boolean' && typeof b === 'boolean') {
16
- return compareBooleans(a, b, ascending);
17
- }
18
-
19
- return 0; // Maintain order for other primitives
20
- }
21
-
22
- export function compareNumbers(
23
- a: number,
24
- b: number,
25
- ascending: boolean
26
- ): number {
27
- if (Number.isNaN(a) && Number.isNaN(b)) return 0;
28
- if (Number.isNaN(a)) return 1;
29
- if (Number.isNaN(b)) return -1;
30
-
31
- return ascending ? a - b : b - a;
32
- }
33
-
34
- export function compareBooleans(
35
- a: boolean,
36
- b: boolean,
37
- ascending: boolean
38
- ): number {
39
- if (a === b) return 0;
40
- if (a) return ascending ? 1 : -1;
41
- return ascending ? -1 : 1;
42
- }
43
-
44
- export function compareObjectKeys(
45
- keyA: string | symbol,
46
- keyB: string | symbol,
47
- ascending: boolean
48
- ): number {
49
- if (typeof keyA === 'symbol' && typeof keyB === 'symbol') return 0;
50
- if (typeof keyA === 'symbol') return 1;
51
- if (typeof keyB === 'symbol') return -1;
52
-
53
- const stringA = keyA as string;
54
- const stringB = keyB as string;
55
-
56
- return ascending
57
- ? stringA.localeCompare(stringB)
58
- : stringB.localeCompare(stringA);
59
- }
package/src/core.ts DELETED
@@ -1,20 +0,0 @@
1
- import type { SortOptions } from './types';
2
- import { isPrimitive, isNonSortableObject, isObject } from './type-guards';
3
- import { sortArray } from './array-sorter';
4
- import { sortObject } from './object-sorter';
5
-
6
- export function sortRecursively<T>(data: T, options: SortOptions): T {
7
- if (Array.isArray(data)) {
8
- return sortArray(data, options) as T;
9
- }
10
-
11
- if (isPrimitive(data) || isNonSortableObject(data)) {
12
- return data;
13
- }
14
-
15
- if (isObject(data)) {
16
- return sortObject(data, options) as T;
17
- }
18
-
19
- return data;
20
- }
package/src/index.ts DELETED
@@ -1,19 +0,0 @@
1
- import type { SortOptions } from './types';
2
- import { sortRecursively } from './core';
3
-
4
- export function sort<T>(
5
- data: T,
6
- ascending = true,
7
- sortPrimitiveArrays = false
8
- ): T {
9
- const options: SortOptions = { ascending, sortPrimitiveArrays };
10
- return sortRecursively(data, options);
11
- }
12
-
13
- // Re-export types for convenience
14
- export type {
15
- SortOptions,
16
- ObjectType,
17
- SortedEntry,
18
- NonSortableType,
19
- } from './types';
@@ -1,41 +0,0 @@
1
- import type { SortOptions, ObjectType, SortedEntry } from './types';
2
- import { compareObjectKeys } from './comparators';
3
-
4
- export function sortObject(obj: ObjectType, options: SortOptions): ObjectType {
5
- const entries = collectObjectEntries(obj);
6
- const sortedEntries = sortObjectEntries(entries, options.ascending);
7
- return createSortedObject(sortedEntries, options);
8
- }
9
-
10
- // Object entry handling
11
- function collectObjectEntries(obj: ObjectType): SortedEntry[] {
12
- const stringEntries = Object.entries(obj);
13
- const symbolEntries = Object.getOwnPropertySymbols(obj).map(
14
- (symbol) => [symbol, obj[symbol]] as SortedEntry
15
- );
16
-
17
- return [...stringEntries, ...symbolEntries];
18
- }
19
-
20
- function sortObjectEntries(
21
- entries: SortedEntry[],
22
- ascending: boolean
23
- ): SortedEntry[] {
24
- return entries.sort(([keyA], [keyB]) =>
25
- compareObjectKeys(keyA, keyB, ascending)
26
- );
27
- }
28
-
29
- function createSortedObject(
30
- entries: SortedEntry[],
31
- options: SortOptions
32
- ): ObjectType {
33
- const sortedEntries = entries.map(([key, value]) => [
34
- key,
35
- sortRecursively(value, options),
36
- ]);
37
-
38
- return Object.fromEntries(sortedEntries);
39
- }
40
-
41
- import { sortRecursively } from './core';
@@ -1,46 +0,0 @@
1
- import { NULLISH_VALUES, SORTABLE_PRIMITIVE_TYPES } from './types';
2
- import type { NullishValue, NonSortableType, ObjectType } from './types';
3
-
4
- // Type guards - single responsibility, clear naming
5
- export function isNullish(value: unknown): value is NullishValue {
6
- return NULLISH_VALUES.includes(value as NullishValue);
7
- }
8
-
9
- export function hasNullishValues(array: unknown[]): boolean {
10
- return array.some(isNullish);
11
- }
12
-
13
- export function isSortablePrimitive(
14
- value: unknown
15
- ): value is string | number | boolean {
16
- return SORTABLE_PRIMITIVE_TYPES.includes(
17
- typeof value as 'string' | 'number' | 'boolean'
18
- );
19
- }
20
-
21
- export function isPrimitive(data: unknown): boolean {
22
- return isSortablePrimitive(data) || isNullish(data);
23
- }
24
-
25
- export function isObject(data: unknown): data is ObjectType {
26
- return typeof data === 'object' && data !== null;
27
- }
28
-
29
- export function isNonSortableObject(obj: unknown): obj is NonSortableType {
30
- const nonSortableTypes = [
31
- Date,
32
- RegExp,
33
- Function,
34
- Error,
35
- Map,
36
- Set,
37
- WeakMap,
38
- WeakSet,
39
- Promise,
40
- ];
41
-
42
- return (
43
- nonSortableTypes.some((type) => obj instanceof type) ||
44
- Symbol.iterator in Object(obj)
45
- );
46
- }
package/src/types.ts DELETED
@@ -1,29 +0,0 @@
1
- // Constants - make the code self-documenting
2
- export const NULLISH_VALUES = [null, undefined] as const;
3
- export const SORTABLE_PRIMITIVE_TYPES = [
4
- 'string',
5
- 'number',
6
- 'boolean',
7
- ] as const;
8
-
9
- // Type definitions that make sense
10
- export type NullishValue = (typeof NULLISH_VALUES)[number];
11
- export type SortablePrimitiveType = (typeof SORTABLE_PRIMITIVE_TYPES)[number];
12
- export type ObjectType = Record<string | symbol, unknown>;
13
- export type SortedEntry = [string | symbol, unknown];
14
- export type NonSortableType =
15
- | Date
16
- | RegExp
17
- | (() => unknown)
18
- | ((...args: unknown[]) => unknown)
19
- | Error
20
- | Map<unknown, unknown>
21
- | Set<unknown>
22
- | WeakMap<object, unknown>
23
- | WeakSet<object>
24
- | Promise<unknown>;
25
-
26
- export type SortOptions = {
27
- ascending: boolean;
28
- sortPrimitiveArrays: boolean;
29
- };
package/tsconfig.json DELETED
@@ -1,11 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "declaration": true,
4
- "esModuleInterop": true,
5
- "strict": true,
6
- "target": "es6",
7
- "moduleResolution": "node"
8
- },
9
- "include": ["src"],
10
- "exclude": ["node_modules"]
11
- }