@tamtamchik/json-deep-sort 1.4.0-beta.3 → 1.4.0-beta.4

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/dist/index.js CHANGED
@@ -24,188 +24,113 @@ __export(index_exports, {
24
24
  });
25
25
  module.exports = __toCommonJS(index_exports);
26
26
 
27
- // src/primitive-array-sorter.ts
28
- var SORTABLE_PRIMITIVE_TYPES = ["string", "number", "boolean"];
27
+ // src/types.ts
29
28
  var NULLISH_VALUES = [null, void 0];
30
- var PrimitiveArraySorter = class {
31
- /**
32
- * Sorts primitive arrays when enabled
33
- */
34
- static sortPrimitiveArray(array, ascending, sortPrimitiveArrays) {
35
- if (!sortPrimitiveArrays || !this.canSortPrimitiveArray(array)) {
36
- return array;
37
- }
38
- return [...array].sort(
39
- (a, b) => this.compareSortablePrimitives(a, b, ascending)
40
- );
41
- }
42
- /**
43
- * Checks if an array can be sorted as primitives
44
- */
45
- static canSortPrimitiveArray(array) {
46
- return this.allItemsAreSortablePrimitives(array) && this.allItemsHaveSameSortableType(array);
47
- }
48
- /**
49
- * Checks if all items are sortable primitives
50
- */
51
- static allItemsAreSortablePrimitives(array) {
52
- return array.every((item) => this.isSortablePrimitive(item));
53
- }
54
- /**
55
- * Checks if all items have the same sortable type
56
- */
57
- static allItemsHaveSameSortableType(array) {
58
- if (array.length === 0) return true;
59
- if (this.hasNullishValues(array)) {
60
- return false;
61
- }
62
- const firstItem = array[0];
63
- if (!this.isSortablePrimitive(firstItem)) return false;
64
- const firstType = typeof firstItem;
65
- return array.every((item) => typeof item === firstType);
66
- }
67
- /**
68
- * Compares two sortable primitive values
69
- */
70
- static compareSortablePrimitives(a, b, ascending) {
71
- if (typeof a === "string" && typeof b === "string") {
72
- return this.compareStrings(a, b, ascending);
73
- }
74
- if (typeof a === "number" && typeof b === "number") {
75
- return this.compareNumbers(a, b, ascending);
76
- }
77
- if (typeof a === "boolean" && typeof b === "boolean") {
78
- return this.compareBooleans(a, b, ascending);
79
- }
80
- return 0;
81
- }
82
- /**
83
- * Compares two strings
84
- */
85
- static compareStrings(a, b, ascending) {
86
- const result = a.localeCompare(b);
87
- return ascending ? result : -result;
88
- }
89
- /**
90
- * Compares two numbers
91
- */
92
- static compareNumbers(a, b, ascending) {
93
- if (isNaN(a) && isNaN(b)) return 0;
94
- if (isNaN(a)) return 1;
95
- if (isNaN(b)) return -1;
96
- const result = a - b;
97
- return ascending ? result : -result;
98
- }
99
- /**
100
- * Compares two booleans
101
- */
102
- static compareBooleans(a, b, ascending) {
103
- if (a === b) return 0;
104
- if (a) return ascending ? 1 : -1;
105
- return ascending ? -1 : 1;
106
- }
107
- /**
108
- * Checks if a value is a sortable primitive
109
- */
110
- static isSortablePrimitive(value) {
111
- return SORTABLE_PRIMITIVE_TYPES.includes(
112
- typeof value
113
- );
114
- }
115
- /**
116
- * Checks if an array has nullish values
117
- */
118
- static hasNullishValues(array) {
119
- return array.some(this.isNullish);
120
- }
121
- /**
122
- * Checks if a value is nullish
123
- */
124
- static isNullish(value) {
125
- return NULLISH_VALUES.includes(value);
126
- }
127
- };
29
+ var SORTABLE_PRIMITIVE_TYPES = [
30
+ "string",
31
+ "number",
32
+ "boolean"
33
+ ];
128
34
 
129
35
  // src/type-guards.ts
130
- var TypeGuards = class {
131
- /**
132
- * Checks if a value is a primitive (string, number, boolean, null, undefined)
133
- */
134
- static isPrimitive(data) {
135
- return this.isSortablePrimitive(data) || this.isNullish(data);
136
- }
137
- /**
138
- * Checks if a value is a sortable primitive (string, number, boolean)
139
- */
140
- static isSortablePrimitive(value) {
141
- const sortableTypes = ["string", "number", "boolean"];
142
- return sortableTypes.includes(typeof value);
143
- }
144
- /**
145
- * Checks if a value is nullish (null or undefined)
146
- */
147
- static isNullish(value) {
148
- return value === null || value === void 0;
149
- }
150
- /**
151
- * Checks if a value is a plain object
152
- */
153
- static isObject(data) {
154
- return typeof data === "object" && data !== null;
155
- }
156
- /**
157
- * Checks if an object is non-sortable (Date, RegExp, Function, etc.)
158
- */
159
- static isNonSortableObject(obj) {
160
- const nonSortableTypes = [
161
- Date,
162
- RegExp,
163
- Function,
164
- Error,
165
- Map,
166
- Set,
167
- WeakMap,
168
- WeakSet,
169
- Promise
170
- ];
171
- return nonSortableTypes.some((type) => obj instanceof type) || Symbol.iterator in Object(obj);
172
- }
173
- /**
174
- * Checks if an array has nullish values
175
- */
176
- static hasNullishValues(array) {
177
- return array.some(this.isNullish);
178
- }
179
- };
180
-
181
- // src/index.ts
182
- function sort(data, ascending = true, sortPrimitiveArrays = false) {
183
- const options = { ascending, sortPrimitiveArrays };
184
- return sortRecursively(data, options);
36
+ function isNullish(value) {
37
+ return NULLISH_VALUES.includes(value);
185
38
  }
186
- function sortRecursively(data, options) {
187
- if (Array.isArray(data)) {
188
- return sortArray(data, options);
39
+ function hasNullishValues(array) {
40
+ return array.some(isNullish);
41
+ }
42
+ function isSortablePrimitive(value) {
43
+ return SORTABLE_PRIMITIVE_TYPES.includes(
44
+ typeof value
45
+ );
46
+ }
47
+ function isPrimitive(data) {
48
+ return isSortablePrimitive(data) || isNullish(data);
49
+ }
50
+ function isObject(data) {
51
+ return typeof data === "object" && data !== null;
52
+ }
53
+ 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
69
+ function compareSortablePrimitives(a, b, ascending) {
70
+ if (typeof a === "string" && typeof b === "string") {
71
+ return ascending ? a.localeCompare(b) : b.localeCompare(a);
189
72
  }
190
- if (TypeGuards.isPrimitive(data) || TypeGuards.isNonSortableObject(data)) {
191
- return data;
73
+ if (typeof a === "number" && typeof b === "number") {
74
+ return compareNumbers(a, b, ascending);
192
75
  }
193
- if (TypeGuards.isObject(data)) {
194
- return sortObject(data, options);
76
+ if (typeof a === "boolean" && typeof b === "boolean") {
77
+ return compareBooleans(a, b, ascending);
195
78
  }
196
- return data;
79
+ return 0;
80
+ }
81
+ 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;
86
+ }
87
+ function compareBooleans(a, b, ascending) {
88
+ if (a === b) return 0;
89
+ if (a) return ascending ? 1 : -1;
90
+ return ascending ? -1 : 1;
91
+ }
92
+ 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);
197
99
  }
100
+
101
+ // src/array-sorter.ts
198
102
  function sortArray(array, options) {
199
- const sortedPrimitiveArray = PrimitiveArraySorter.sortPrimitiveArray(
200
- array,
201
- options.ascending,
202
- options.sortPrimitiveArrays
203
- );
204
- if (sortedPrimitiveArray !== array) {
205
- return sortedPrimitiveArray;
103
+ if (shouldSortPrimitiveArray(array, options.sortPrimitiveArrays)) {
104
+ return sortPrimitiveArray(array, options.ascending);
206
105
  }
207
106
  return array.map((item) => sortRecursively(item, options));
208
107
  }
108
+ function shouldSortPrimitiveArray(array, sortPrimitiveArrays) {
109
+ return sortPrimitiveArrays && canSortPrimitiveArray(array);
110
+ }
111
+ function canSortPrimitiveArray(array) {
112
+ return allItemsAreSortablePrimitives(array) && allItemsHaveSameSortableType(array);
113
+ }
114
+ function allItemsAreSortablePrimitives(array) {
115
+ return array.every(isSortablePrimitive);
116
+ }
117
+ function sortPrimitiveArray(array, ascending) {
118
+ return [...array].sort((a, b) => compareSortablePrimitives(a, b, ascending));
119
+ }
120
+ 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
209
134
  function sortObject(obj, options) {
210
135
  const entries = collectObjectEntries(obj);
211
136
  const sortedEntries = sortObjectEntries(entries, options.ascending);
@@ -223,14 +148,6 @@ function sortObjectEntries(entries, ascending) {
223
148
  ([keyA], [keyB]) => compareObjectKeys(keyA, keyB, ascending)
224
149
  );
225
150
  }
226
- function compareObjectKeys(keyA, keyB, ascending) {
227
- if (typeof keyA === "symbol" && typeof keyB === "symbol") return 0;
228
- if (typeof keyA === "symbol") return 1;
229
- if (typeof keyB === "symbol") return -1;
230
- const stringA = keyA;
231
- const stringB = keyB;
232
- return ascending ? stringA.localeCompare(stringB) : stringB.localeCompare(stringA);
233
- }
234
151
  function createSortedObject(entries, options) {
235
152
  const sortedEntries = entries.map(([key, value]) => [
236
153
  key,
@@ -238,6 +155,26 @@ function createSortedObject(entries, options) {
238
155
  ]);
239
156
  return Object.fromEntries(sortedEntries);
240
157
  }
158
+
159
+ // src/core.ts
160
+ 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
174
+ function sort(data, ascending = true, sortPrimitiveArrays = false) {
175
+ const options = { ascending, sortPrimitiveArrays };
176
+ return sortRecursively(data, options);
177
+ }
241
178
  // Annotate the CommonJS export names for ESM import in node:
242
179
  0 && (module.exports = {
243
180
  sort
package/dist/index.mjs CHANGED
@@ -1,185 +1,110 @@
1
- // src/primitive-array-sorter.ts
2
- var SORTABLE_PRIMITIVE_TYPES = ["string", "number", "boolean"];
1
+ // src/types.ts
3
2
  var NULLISH_VALUES = [null, void 0];
4
- var PrimitiveArraySorter = class {
5
- /**
6
- * Sorts primitive arrays when enabled
7
- */
8
- static sortPrimitiveArray(array, ascending, sortPrimitiveArrays) {
9
- if (!sortPrimitiveArrays || !this.canSortPrimitiveArray(array)) {
10
- return array;
11
- }
12
- return [...array].sort(
13
- (a, b) => this.compareSortablePrimitives(a, b, ascending)
14
- );
15
- }
16
- /**
17
- * Checks if an array can be sorted as primitives
18
- */
19
- static canSortPrimitiveArray(array) {
20
- return this.allItemsAreSortablePrimitives(array) && this.allItemsHaveSameSortableType(array);
21
- }
22
- /**
23
- * Checks if all items are sortable primitives
24
- */
25
- static allItemsAreSortablePrimitives(array) {
26
- return array.every((item) => this.isSortablePrimitive(item));
27
- }
28
- /**
29
- * Checks if all items have the same sortable type
30
- */
31
- static allItemsHaveSameSortableType(array) {
32
- if (array.length === 0) return true;
33
- if (this.hasNullishValues(array)) {
34
- return false;
35
- }
36
- const firstItem = array[0];
37
- if (!this.isSortablePrimitive(firstItem)) return false;
38
- const firstType = typeof firstItem;
39
- return array.every((item) => typeof item === firstType);
40
- }
41
- /**
42
- * Compares two sortable primitive values
43
- */
44
- static compareSortablePrimitives(a, b, ascending) {
45
- if (typeof a === "string" && typeof b === "string") {
46
- return this.compareStrings(a, b, ascending);
47
- }
48
- if (typeof a === "number" && typeof b === "number") {
49
- return this.compareNumbers(a, b, ascending);
50
- }
51
- if (typeof a === "boolean" && typeof b === "boolean") {
52
- return this.compareBooleans(a, b, ascending);
53
- }
54
- return 0;
55
- }
56
- /**
57
- * Compares two strings
58
- */
59
- static compareStrings(a, b, ascending) {
60
- const result = a.localeCompare(b);
61
- return ascending ? result : -result;
62
- }
63
- /**
64
- * Compares two numbers
65
- */
66
- static compareNumbers(a, b, ascending) {
67
- if (isNaN(a) && isNaN(b)) return 0;
68
- if (isNaN(a)) return 1;
69
- if (isNaN(b)) return -1;
70
- const result = a - b;
71
- return ascending ? result : -result;
72
- }
73
- /**
74
- * Compares two booleans
75
- */
76
- static compareBooleans(a, b, ascending) {
77
- if (a === b) return 0;
78
- if (a) return ascending ? 1 : -1;
79
- return ascending ? -1 : 1;
80
- }
81
- /**
82
- * Checks if a value is a sortable primitive
83
- */
84
- static isSortablePrimitive(value) {
85
- return SORTABLE_PRIMITIVE_TYPES.includes(
86
- typeof value
87
- );
88
- }
89
- /**
90
- * Checks if an array has nullish values
91
- */
92
- static hasNullishValues(array) {
93
- return array.some(this.isNullish);
94
- }
95
- /**
96
- * Checks if a value is nullish
97
- */
98
- static isNullish(value) {
99
- return NULLISH_VALUES.includes(value);
100
- }
101
- };
3
+ var SORTABLE_PRIMITIVE_TYPES = [
4
+ "string",
5
+ "number",
6
+ "boolean"
7
+ ];
102
8
 
103
9
  // src/type-guards.ts
104
- var TypeGuards = class {
105
- /**
106
- * Checks if a value is a primitive (string, number, boolean, null, undefined)
107
- */
108
- static isPrimitive(data) {
109
- return this.isSortablePrimitive(data) || this.isNullish(data);
110
- }
111
- /**
112
- * Checks if a value is a sortable primitive (string, number, boolean)
113
- */
114
- static isSortablePrimitive(value) {
115
- const sortableTypes = ["string", "number", "boolean"];
116
- return sortableTypes.includes(typeof value);
117
- }
118
- /**
119
- * Checks if a value is nullish (null or undefined)
120
- */
121
- static isNullish(value) {
122
- return value === null || value === void 0;
123
- }
124
- /**
125
- * Checks if a value is a plain object
126
- */
127
- static isObject(data) {
128
- return typeof data === "object" && data !== null;
129
- }
130
- /**
131
- * Checks if an object is non-sortable (Date, RegExp, Function, etc.)
132
- */
133
- static isNonSortableObject(obj) {
134
- const nonSortableTypes = [
135
- Date,
136
- RegExp,
137
- Function,
138
- Error,
139
- Map,
140
- Set,
141
- WeakMap,
142
- WeakSet,
143
- Promise
144
- ];
145
- return nonSortableTypes.some((type) => obj instanceof type) || Symbol.iterator in Object(obj);
146
- }
147
- /**
148
- * Checks if an array has nullish values
149
- */
150
- static hasNullishValues(array) {
151
- return array.some(this.isNullish);
152
- }
153
- };
154
-
155
- // src/index.ts
156
- function sort(data, ascending = true, sortPrimitiveArrays = false) {
157
- const options = { ascending, sortPrimitiveArrays };
158
- return sortRecursively(data, options);
10
+ function isNullish(value) {
11
+ return NULLISH_VALUES.includes(value);
159
12
  }
160
- function sortRecursively(data, options) {
161
- if (Array.isArray(data)) {
162
- return sortArray(data, options);
13
+ function hasNullishValues(array) {
14
+ return array.some(isNullish);
15
+ }
16
+ function isSortablePrimitive(value) {
17
+ return SORTABLE_PRIMITIVE_TYPES.includes(
18
+ typeof value
19
+ );
20
+ }
21
+ function isPrimitive(data) {
22
+ return isSortablePrimitive(data) || isNullish(data);
23
+ }
24
+ function isObject(data) {
25
+ return typeof data === "object" && data !== null;
26
+ }
27
+ 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
43
+ function compareSortablePrimitives(a, b, ascending) {
44
+ if (typeof a === "string" && typeof b === "string") {
45
+ return ascending ? a.localeCompare(b) : b.localeCompare(a);
163
46
  }
164
- if (TypeGuards.isPrimitive(data) || TypeGuards.isNonSortableObject(data)) {
165
- return data;
47
+ if (typeof a === "number" && typeof b === "number") {
48
+ return compareNumbers(a, b, ascending);
166
49
  }
167
- if (TypeGuards.isObject(data)) {
168
- return sortObject(data, options);
50
+ if (typeof a === "boolean" && typeof b === "boolean") {
51
+ return compareBooleans(a, b, ascending);
169
52
  }
170
- return data;
53
+ return 0;
54
+ }
55
+ 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;
60
+ }
61
+ function compareBooleans(a, b, ascending) {
62
+ if (a === b) return 0;
63
+ if (a) return ascending ? 1 : -1;
64
+ return ascending ? -1 : 1;
65
+ }
66
+ 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);
171
73
  }
74
+
75
+ // src/array-sorter.ts
172
76
  function sortArray(array, options) {
173
- const sortedPrimitiveArray = PrimitiveArraySorter.sortPrimitiveArray(
174
- array,
175
- options.ascending,
176
- options.sortPrimitiveArrays
177
- );
178
- if (sortedPrimitiveArray !== array) {
179
- return sortedPrimitiveArray;
77
+ if (shouldSortPrimitiveArray(array, options.sortPrimitiveArrays)) {
78
+ return sortPrimitiveArray(array, options.ascending);
180
79
  }
181
80
  return array.map((item) => sortRecursively(item, options));
182
81
  }
82
+ function shouldSortPrimitiveArray(array, sortPrimitiveArrays) {
83
+ return sortPrimitiveArrays && canSortPrimitiveArray(array);
84
+ }
85
+ function canSortPrimitiveArray(array) {
86
+ return allItemsAreSortablePrimitives(array) && allItemsHaveSameSortableType(array);
87
+ }
88
+ function allItemsAreSortablePrimitives(array) {
89
+ return array.every(isSortablePrimitive);
90
+ }
91
+ function sortPrimitiveArray(array, ascending) {
92
+ return [...array].sort((a, b) => compareSortablePrimitives(a, b, ascending));
93
+ }
94
+ 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
183
108
  function sortObject(obj, options) {
184
109
  const entries = collectObjectEntries(obj);
185
110
  const sortedEntries = sortObjectEntries(entries, options.ascending);
@@ -197,14 +122,6 @@ function sortObjectEntries(entries, ascending) {
197
122
  ([keyA], [keyB]) => compareObjectKeys(keyA, keyB, ascending)
198
123
  );
199
124
  }
200
- function compareObjectKeys(keyA, keyB, ascending) {
201
- if (typeof keyA === "symbol" && typeof keyB === "symbol") return 0;
202
- if (typeof keyA === "symbol") return 1;
203
- if (typeof keyB === "symbol") return -1;
204
- const stringA = keyA;
205
- const stringB = keyB;
206
- return ascending ? stringA.localeCompare(stringB) : stringB.localeCompare(stringA);
207
- }
208
125
  function createSortedObject(entries, options) {
209
126
  const sortedEntries = entries.map(([key, value]) => [
210
127
  key,
@@ -212,6 +129,26 @@ function createSortedObject(entries, options) {
212
129
  ]);
213
130
  return Object.fromEntries(sortedEntries);
214
131
  }
132
+
133
+ // src/core.ts
134
+ 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
148
+ function sort(data, ascending = true, sortPrimitiveArrays = false) {
149
+ const options = { ascending, sortPrimitiveArrays };
150
+ return sortRecursively(data, options);
151
+ }
215
152
  export {
216
153
  sort
217
154
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tamtamchik/json-deep-sort",
3
- "version": "1.4.0-beta.3",
3
+ "version": "1.4.0-beta.4",
4
4
  "description": "A comprehensive utility for sorting JSON objects by keys.",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -0,0 +1,61 @@
1
+ import { 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';
@@ -0,0 +1,59 @@
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 ADDED
@@ -0,0 +1,20 @@
1
+ import { 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 CHANGED
@@ -1,14 +1,5 @@
1
- import { PrimitiveArraySorter } from './primitive-array-sorter';
2
- import { TypeGuards } from './type-guards';
3
-
4
- // Type definitions
5
- type ObjectType = Record<string | symbol, unknown>;
6
- type SortedEntry = [string | symbol, unknown];
7
-
8
- type SortOptions = {
9
- ascending: boolean;
10
- sortPrimitiveArrays: boolean;
11
- };
1
+ import { SortOptions } from './types';
2
+ import { sortRecursively } from './core';
12
3
 
13
4
  export function sort<T>(
14
5
  data: T,
@@ -19,87 +10,10 @@ export function sort<T>(
19
10
  return sortRecursively(data, options);
20
11
  }
21
12
 
22
- function sortRecursively<T>(data: T, options: SortOptions): T {
23
- if (Array.isArray(data)) {
24
- return sortArray(data, options) as T;
25
- }
26
-
27
- if (TypeGuards.isPrimitive(data) || TypeGuards.isNonSortableObject(data)) {
28
- return data;
29
- }
30
-
31
- if (TypeGuards.isObject(data)) {
32
- return sortObject(data, options) as T;
33
- }
34
-
35
- return data;
36
- }
37
-
38
- function sortArray<T>(array: T[], options: SortOptions): T[] {
39
- // Use the extracted PrimitiveArraySorter
40
- const sortedPrimitiveArray = PrimitiveArraySorter.sortPrimitiveArray(
41
- array,
42
- options.ascending,
43
- options.sortPrimitiveArrays
44
- );
45
-
46
- if (sortedPrimitiveArray !== array) {
47
- return sortedPrimitiveArray as T[];
48
- }
49
-
50
- // Recursively sort nested items
51
- return array.map((item) => sortRecursively(item, options));
52
- }
53
-
54
- function sortObject(obj: ObjectType, options: SortOptions): ObjectType {
55
- const entries = collectObjectEntries(obj);
56
- const sortedEntries = sortObjectEntries(entries, options.ascending);
57
- return createSortedObject(sortedEntries, options);
58
- }
59
-
60
- function collectObjectEntries(obj: ObjectType): SortedEntry[] {
61
- const stringEntries = Object.entries(obj);
62
- const symbolEntries = Object.getOwnPropertySymbols(obj).map(
63
- (symbol) => [symbol, obj[symbol]] as SortedEntry
64
- );
65
-
66
- return [...stringEntries, ...symbolEntries];
67
- }
68
-
69
- function sortObjectEntries(
70
- entries: SortedEntry[],
71
- ascending: boolean
72
- ): SortedEntry[] {
73
- return entries.sort(([keyA], [keyB]) =>
74
- compareObjectKeys(keyA, keyB, ascending)
75
- );
76
- }
77
-
78
- function compareObjectKeys(
79
- keyA: string | symbol,
80
- keyB: string | symbol,
81
- ascending: boolean
82
- ): number {
83
- if (typeof keyA === 'symbol' && typeof keyB === 'symbol') return 0;
84
- if (typeof keyA === 'symbol') return 1;
85
- if (typeof keyB === 'symbol') return -1;
86
-
87
- const stringA = keyA as string;
88
- const stringB = keyB as string;
89
-
90
- return ascending
91
- ? stringA.localeCompare(stringB)
92
- : stringB.localeCompare(stringA);
93
- }
94
-
95
- function createSortedObject(
96
- entries: SortedEntry[],
97
- options: SortOptions
98
- ): ObjectType {
99
- const sortedEntries = entries.map(([key, value]) => [
100
- key,
101
- sortRecursively(value, options),
102
- ]);
103
-
104
- return Object.fromEntries(sortedEntries);
105
- }
13
+ // Re-export types for convenience
14
+ export type {
15
+ SortOptions,
16
+ ObjectType,
17
+ SortedEntry,
18
+ NonSortableType,
19
+ } from './types';
@@ -1,72 +1,41 @@
1
- // Type definitions for object sorting
2
- type ObjectType = Record<string | symbol, unknown>;
3
- type SortedEntry = [string | symbol, unknown];
1
+ import { SortOptions, ObjectType, SortedEntry } from './types';
2
+ import { compareObjectKeys } from './comparators';
4
3
 
5
- export class ObjectSorter {
6
- /**
7
- * Sorts object keys recursively
8
- */
9
- static sortObject(
10
- obj: ObjectType,
11
- options: { ascending: boolean }
12
- ): ObjectType {
13
- const entries = this.collectObjectEntries(obj);
14
- const sortedEntries = this.sortObjectEntries(entries, options.ascending);
15
- return this.createSortedObject(sortedEntries);
16
- }
17
-
18
- /**
19
- * Collects all object entries (string and symbol keys)
20
- */
21
- private static collectObjectEntries(obj: ObjectType): SortedEntry[] {
22
- const stringEntries = Object.entries(obj);
23
- const symbolEntries = Object.getOwnPropertySymbols(obj).map(
24
- (symbol) => [symbol, obj[symbol]] as SortedEntry
25
- );
26
-
27
- return [...stringEntries, ...symbolEntries];
28
- }
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
+ }
29
9
 
30
- /**
31
- * Sorts object entries by key
32
- */
33
- private static sortObjectEntries(
34
- entries: SortedEntry[],
35
- ascending: boolean
36
- ): SortedEntry[] {
37
- return entries.sort(([keyA], [keyB]) =>
38
- this.compareObjectKeys(keyA, keyB, ascending)
39
- );
40
- }
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
+ );
41
16
 
42
- /**
43
- * Compares two object keys for sorting
44
- */
45
- private static compareObjectKeys(
46
- keyA: string | symbol,
47
- keyB: string | symbol,
48
- ascending: boolean
49
- ): number {
50
- // Symbols are always sorted after strings
51
- if (typeof keyA === 'symbol' && typeof keyB === 'symbol') return 0;
52
- if (typeof keyA === 'symbol') return 1;
53
- if (typeof keyB === 'symbol') return -1;
17
+ return [...stringEntries, ...symbolEntries];
18
+ }
54
19
 
55
- // Both are strings, use localeCompare
56
- const stringA = keyA as string;
57
- const stringB = keyB as string;
20
+ function sortObjectEntries(
21
+ entries: SortedEntry[],
22
+ ascending: boolean
23
+ ): SortedEntry[] {
24
+ return entries.sort(([keyA], [keyB]) =>
25
+ compareObjectKeys(keyA, keyB, ascending)
26
+ );
27
+ }
58
28
 
59
- return ascending
60
- ? stringA.localeCompare(stringB)
61
- : stringB.localeCompare(stringA);
62
- }
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
+ ]);
63
37
 
64
- /**
65
- * Creates a new object with sorted entries
66
- */
67
- private static createSortedObject(entries: SortedEntry[]): ObjectType {
68
- // Note: This is a placeholder - the actual implementation will be in the main file
69
- // since it needs access to the recursive sorting function
70
- return Object.fromEntries(entries);
71
- }
38
+ return Object.fromEntries(sortedEntries);
72
39
  }
40
+
41
+ import { sortRecursively } from './core';
@@ -1,74 +1,51 @@
1
- // Type definitions for type guards
2
- type NonSortableType =
3
- | Date
4
- | RegExp
5
- | (() => unknown)
6
- | ((...args: unknown[]) => unknown)
7
- | Error
8
- | Map<unknown, unknown>
9
- | Set<unknown>
10
- | WeakMap<object, unknown>
11
- | WeakSet<object>
12
- | Promise<unknown>;
1
+ import {
2
+ NULLISH_VALUES,
3
+ SORTABLE_PRIMITIVE_TYPES,
4
+ NullishValue,
5
+ NonSortableType,
6
+ ObjectType,
7
+ } from './types';
13
8
 
14
- export class TypeGuards {
15
- /**
16
- * Checks if a value is a primitive (string, number, boolean, null, undefined)
17
- */
18
- static isPrimitive(data: unknown): boolean {
19
- return this.isSortablePrimitive(data) || this.isNullish(data);
20
- }
9
+ // Type guards - single responsibility, clear naming
10
+ export function isNullish(value: unknown): value is NullishValue {
11
+ return NULLISH_VALUES.includes(value as NullishValue);
12
+ }
21
13
 
22
- /**
23
- * Checks if a value is a sortable primitive (string, number, boolean)
24
- */
25
- static isSortablePrimitive(
26
- value: unknown
27
- ): value is string | number | boolean {
28
- const sortableTypes = ['string', 'number', 'boolean'];
29
- return sortableTypes.includes(typeof value);
30
- }
14
+ export function hasNullishValues(array: unknown[]): boolean {
15
+ return array.some(isNullish);
16
+ }
31
17
 
32
- /**
33
- * Checks if a value is nullish (null or undefined)
34
- */
35
- static isNullish(value: unknown): value is null | undefined {
36
- return value === null || value === undefined;
37
- }
18
+ export function isSortablePrimitive(
19
+ value: unknown
20
+ ): value is string | number | boolean {
21
+ return SORTABLE_PRIMITIVE_TYPES.includes(
22
+ typeof value as 'string' | 'number' | 'boolean'
23
+ );
24
+ }
38
25
 
39
- /**
40
- * Checks if a value is a plain object
41
- */
42
- static isObject(data: unknown): data is Record<string | symbol, unknown> {
43
- return typeof data === 'object' && data !== null;
44
- }
26
+ export function isPrimitive(data: unknown): boolean {
27
+ return isSortablePrimitive(data) || isNullish(data);
28
+ }
45
29
 
46
- /**
47
- * Checks if an object is non-sortable (Date, RegExp, Function, etc.)
48
- */
49
- static isNonSortableObject(obj: unknown): obj is NonSortableType {
50
- const nonSortableTypes = [
51
- Date,
52
- RegExp,
53
- Function,
54
- Error,
55
- Map,
56
- Set,
57
- WeakMap,
58
- WeakSet,
59
- Promise,
60
- ];
30
+ export function isObject(data: unknown): data is ObjectType {
31
+ return typeof data === 'object' && data !== null;
32
+ }
61
33
 
62
- return (
63
- nonSortableTypes.some((type) => obj instanceof type) ||
64
- Symbol.iterator in Object(obj)
65
- );
66
- }
34
+ export function isNonSortableObject(obj: unknown): obj is NonSortableType {
35
+ const nonSortableTypes = [
36
+ Date,
37
+ RegExp,
38
+ Function,
39
+ Error,
40
+ Map,
41
+ Set,
42
+ WeakMap,
43
+ WeakSet,
44
+ Promise,
45
+ ];
67
46
 
68
- /**
69
- * Checks if an array has nullish values
70
- */
71
- static hasNullishValues(array: unknown[]): boolean {
72
- return array.some(this.isNullish);
73
- }
47
+ return (
48
+ nonSortableTypes.some((type) => obj instanceof type) ||
49
+ Symbol.iterator in Object(obj)
50
+ );
74
51
  }
package/src/types.ts ADDED
@@ -0,0 +1,29 @@
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
+ };
@@ -1,151 +0,0 @@
1
- // Constants for primitive array sorting
2
- const SORTABLE_PRIMITIVE_TYPES = ['string', 'number', 'boolean'] as const;
3
- const NULLISH_VALUES = [null, undefined] as const;
4
-
5
- // Type definitions
6
- type SortablePrimitiveType = (typeof SORTABLE_PRIMITIVE_TYPES)[number];
7
- type NullishValue = (typeof NULLISH_VALUES)[number];
8
-
9
- export class PrimitiveArraySorter {
10
- /**
11
- * Sorts primitive arrays when enabled
12
- */
13
- static sortPrimitiveArray(
14
- array: unknown[],
15
- ascending: boolean,
16
- sortPrimitiveArrays: boolean
17
- ): unknown[] {
18
- if (!sortPrimitiveArrays || !this.canSortPrimitiveArray(array)) {
19
- return array;
20
- }
21
-
22
- return [...array].sort((a, b) =>
23
- this.compareSortablePrimitives(a, b, ascending)
24
- );
25
- }
26
-
27
- /**
28
- * Checks if an array can be sorted as primitives
29
- */
30
- private static canSortPrimitiveArray(array: unknown[]): boolean {
31
- return (
32
- this.allItemsAreSortablePrimitives(array) &&
33
- this.allItemsHaveSameSortableType(array)
34
- );
35
- }
36
-
37
- /**
38
- * Checks if all items are sortable primitives
39
- */
40
- private static allItemsAreSortablePrimitives(array: unknown[]): boolean {
41
- return array.every((item) => this.isSortablePrimitive(item));
42
- }
43
-
44
- /**
45
- * Checks if all items have the same sortable type
46
- */
47
- private static allItemsHaveSameSortableType(array: unknown[]): boolean {
48
- if (array.length === 0) return true;
49
-
50
- // Don't sort arrays with null/undefined values
51
- if (this.hasNullishValues(array)) {
52
- return false;
53
- }
54
-
55
- const firstItem = array[0];
56
- if (!this.isSortablePrimitive(firstItem)) return false;
57
-
58
- const firstType = typeof firstItem;
59
- return array.every((item) => typeof item === firstType);
60
- }
61
-
62
- /**
63
- * Compares two sortable primitive values
64
- */
65
- private static compareSortablePrimitives(
66
- a: unknown,
67
- b: unknown,
68
- ascending: boolean
69
- ): number {
70
- if (typeof a === 'string' && typeof b === 'string') {
71
- return this.compareStrings(a, b, ascending);
72
- }
73
-
74
- if (typeof a === 'number' && typeof b === 'number') {
75
- return this.compareNumbers(a, b, ascending);
76
- }
77
-
78
- if (typeof a === 'boolean' && typeof b === 'boolean') {
79
- return this.compareBooleans(a, b, ascending);
80
- }
81
-
82
- // Fallback for mixed types
83
- return 0;
84
- }
85
-
86
- /**
87
- * Compares two strings
88
- */
89
- private static compareStrings(
90
- a: string,
91
- b: string,
92
- ascending: boolean
93
- ): number {
94
- const result = a.localeCompare(b);
95
- return ascending ? result : -result;
96
- }
97
-
98
- /**
99
- * Compares two numbers
100
- */
101
- private static compareNumbers(
102
- a: number,
103
- b: number,
104
- ascending: boolean
105
- ): number {
106
- if (isNaN(a) && isNaN(b)) return 0;
107
- if (isNaN(a)) return 1; // NaN always goes to the end
108
- if (isNaN(b)) return -1; // NaN always goes to the end
109
-
110
- const result = a - b;
111
- return ascending ? result : -result;
112
- }
113
-
114
- /**
115
- * Compares two booleans
116
- */
117
- private static compareBooleans(
118
- a: boolean,
119
- b: boolean,
120
- ascending: boolean
121
- ): number {
122
- if (a === b) return 0;
123
- if (a) return ascending ? 1 : -1;
124
- return ascending ? -1 : 1;
125
- }
126
-
127
- /**
128
- * Checks if a value is a sortable primitive
129
- */
130
- private static isSortablePrimitive(
131
- value: unknown
132
- ): value is string | number | boolean {
133
- return SORTABLE_PRIMITIVE_TYPES.includes(
134
- typeof value as SortablePrimitiveType
135
- );
136
- }
137
-
138
- /**
139
- * Checks if an array has nullish values
140
- */
141
- private static hasNullishValues(array: unknown[]): boolean {
142
- return array.some(this.isNullish);
143
- }
144
-
145
- /**
146
- * Checks if a value is nullish
147
- */
148
- private static isNullish(value: unknown): value is NullishValue {
149
- return NULLISH_VALUES.includes(value as NullishValue);
150
- }
151
- }