(a: any, b: any, opts: CompareOptions)
| 38 | * Always sorts null/undefined values first |
| 39 | */ |
| 40 | export const ascComparator = (a: any, b: any, opts: CompareOptions): number => { |
| 41 | const { nulls } = opts |
| 42 | |
| 43 | // Handle null/undefined |
| 44 | if (a == null && b == null) return 0 |
| 45 | if (a == null) return nulls === `first` ? -1 : 1 |
| 46 | if (b == null) return nulls === `first` ? 1 : -1 |
| 47 | |
| 48 | // Handle NaN / invalid Dates. Following PostgreSQL float semantics, they are |
| 49 | // all equal and sort greater than every other non-null value. This keeps the |
| 50 | // order total (NaN would otherwise compare equal to everything), so such |
| 51 | // values can be sorted and stored in tree-based indexes. |
| 52 | const aUnordered = isUnorderable(a) |
| 53 | const bUnordered = isUnorderable(b) |
| 54 | if (aUnordered && bUnordered) return 0 |
| 55 | if (aUnordered) return 1 |
| 56 | if (bUnordered) return -1 |
| 57 | |
| 58 | // if a and b are both strings, compare them based on locale |
| 59 | if (typeof a === `string` && typeof b === `string`) { |
| 60 | if (opts.stringSort === `locale`) { |
| 61 | return a.localeCompare(b, opts.locale, opts.localeOptions) |
| 62 | } |
| 63 | // For lexical sort we rely on direct comparison for primitive values |
| 64 | } |
| 65 | |
| 66 | // if a and b are both arrays, compare them element by element |
| 67 | if (Array.isArray(a) && Array.isArray(b)) { |
| 68 | for (let i = 0; i < Math.min(a.length, b.length); i++) { |
| 69 | const result = ascComparator(a[i], b[i], opts) |
| 70 | if (result !== 0) { |
| 71 | return result |
| 72 | } |
| 73 | } |
| 74 | // All elements are equal up to the minimum length |
| 75 | return a.length - b.length |
| 76 | } |
| 77 | |
| 78 | // If both are dates, compare them |
| 79 | if (a instanceof Date && b instanceof Date) { |
| 80 | return a.getTime() - b.getTime() |
| 81 | } |
| 82 | |
| 83 | // If both are Temporal objects of the same type, compare by string representation |
| 84 | if (isTemporal(a) && isTemporal(b)) { |
| 85 | const aStr = a.toString() |
| 86 | const bStr = b.toString() |
| 87 | if (aStr < bStr) return -1 |
| 88 | if (aStr > bStr) return 1 |
| 89 | return 0 |
| 90 | } |
| 91 | |
| 92 | // If at least one of the values is an object, use stable IDs for comparison |
| 93 | const aIsObject = typeof a === `object` |
| 94 | const bIsObject = typeof b === `object` |
| 95 | |
| 96 | if (aIsObject || bIsObject) { |
| 97 | // If both are objects, compare their stable IDs |
no test coverage detected
searching dependent graphs…