Skip to content

Commit

Permalink
fix(utils): prettify toStringKey output
Browse files Browse the repository at this point in the history
  • Loading branch information
artalar committed Oct 8, 2024
1 parent 4c9ea78 commit f7f9189
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 11 deletions.
30 changes: 23 additions & 7 deletions packages/utils/src/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
import { test } from 'uvu'
import * as assert from 'uvu/assert'

import { isDeepEqual, toAbortError } from './'
import { isDeepEqual, toAbortError, toStringKey, random, mockRandom } from './'

test('isDeepEqual Set', () => {
assert.ok(
isDeepEqual(new Set([{ a: 1 }, { a: 2 }]), new Set([{ a: 1 }, { a: 2 }])),
)
assert.not.ok(
isDeepEqual(new Set([{ a: 1 }, { a: 2 }]), new Set([{ a: 2 }, { a: 1 }])),
)
assert.ok(isDeepEqual(new Set([{ a: 1 }, { a: 2 }]), new Set([{ a: 1 }, { a: 2 }])))
assert.not.ok(isDeepEqual(new Set([{ a: 1 }, { a: 2 }]), new Set([{ a: 2 }, { a: 1 }])))
;('👍') //?
})

Expand Down Expand Up @@ -56,4 +52,24 @@ test('toAbortError', () => {
;('👍') //?
})

test('toStringKey', () => {
const CLASS = new AbortController()

const obj: Record<string, any> = {}
obj.obj = obj
obj.one = { two: { CLASS } }
obj.list = [1, 2, 3, new Map([['key', 'val']])]

const target = `[object Object][object Array][string]list[object Array][number]1[number]2[number]3[object Map][object Array][string]key[string]val[object Array][string]obj[object Object#1][object Array][string]one[object Object][object Array][string]two[object Object][object Array][string]CLASS[object AbortController#12]`

let i = 1
const unmock = mockRandom(() => i++)

assert.is(toStringKey(obj), target)
assert.is(toStringKey(obj), toStringKey(obj))

unmock()
assert.is(toStringKey(obj), toStringKey(obj))
})

test.run()
22 changes: 18 additions & 4 deletions packages/utils/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,8 +157,18 @@ export const omit = <T, K extends keyof T>(target: T, keys: Array<K>): Plain<Omi
*/
export const jsonClone = <T>(value: T): T => JSON.parse(JSON.stringify(value))

let _random = (min = 0, max = Number.MAX_SAFE_INTEGER - 1) => Math.floor(Math.random() * (max - min + 1)) + min
/** Get random integer. Parameters should be integers too. */
export const random = (min = 0, max = Number.MAX_SAFE_INTEGER - 1) => Math.floor(Math.random() * (max - min + 1)) + min
export const random: typeof _random = (min, max) => _random(min, max)

/** Pass a callback to replace the exported random function. Returned function restores the original random behavior. */
export const mockRandom = (fn: typeof random) => {
const origin = _random
_random = fn
return () => {
_random = origin
}
}

/**
* Asserts that the value is not `null` or `undefined`.
Expand All @@ -181,14 +191,14 @@ export const toStringKey = (thing: any, immutable = true): string => {
var isNominal = tag === 'function' || tag === 'symbol'

if (!isNominal && (tag !== 'object' || thing === null || thing instanceof Date || thing instanceof RegExp)) {
return tag + thing
return `[${tag}]` + thing
}

if (visited.has(thing)) return visited.get(thing)!

// get a unique prefix for each type to separate same array / map
var result = toString.call(thing)
var unique = result + random()
var unique = `${result.slice(0, -1)}#${random()}]`
// thing could be a circular or not stringifiable object from a userspace
visited.set(thing, unique)

Expand All @@ -199,7 +209,11 @@ export const toStringKey = (thing: any, immutable = true): string => {
for (let item of Symbol.iterator in thing ? thing : Object.entries(thing).sort(([a], [b]) => a.localeCompare(b)))
result += toStringKey(item, immutable)

immutable ? visited.set(thing, result) : visited.delete(thing)
if (immutable) {
visited.set(thing, result)
} else {
visited.delete(thing)
}

return result
}
Expand Down

0 comments on commit f7f9189

Please sign in to comment.