Skip to content

Commit

Permalink
feat(lens): support circular references in parseAtoms
Browse files Browse the repository at this point in the history
  • Loading branch information
krulod committed May 22, 2024
1 parent bcd4837 commit 5032ff1
Show file tree
Hide file tree
Showing 2 changed files with 22 additions and 32 deletions.
6 changes: 1 addition & 5 deletions packages/lens/src/parseAtoms.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ test('should parse deep structures', () => {
;`👍` //?
})

test.only('cached references', () => {
test('circular references', () => {
const a = atom([1])
const aRec = { a }
const b = atom([2])
Expand All @@ -160,13 +160,9 @@ test.only('cached references', () => {
const ctx = createTestCtx()

const snapshot = parseAtoms(ctx, c)
console.log({ snapshot })
assert.is(snapshot, parseAtoms(ctx, c))

a(ctx, [10])
assert.is.not(snapshot, parseAtoms(ctx, c))
assert.is.not(snapshot.a, parseAtoms(ctx, c).a)
assert.is(snapshot.b, parseAtoms(ctx, c).b)
})

test.run()
48 changes: 21 additions & 27 deletions packages/lens/src/parseAtoms.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Atom, Ctx, isAtom, Rec } from '@reatom/core'
import { atom, Atom, Ctx, isAtom, Rec } from '@reatom/core'
import { isRec } from '@reatom/utils'

export type ParseAtoms<T> = T extends Atom<infer T>
Expand All @@ -13,37 +13,31 @@ export type ParseAtoms<T> = T extends Atom<infer T>
}
: T

export const parseAtoms = <Value>(
ctx: Ctx,
value: Value,
): ParseAtoms<Value> => {
while (isAtom(value)) value = ctx.spy ? ctx.spy(value) : ctx.get(value)
export const parseAtoms = <T>(ctx: Ctx, value: T) => unwrap(ctx, value, new WeakMap)

const unwrap = <T>(ctx: Ctx, value: T, cache: WeakMap<any, any>): ParseAtoms<T> => {
while (isAtom(value)) value = ctx.spy ? ctx.spy(value) : ctx.get(value)

if (typeof value !== 'object' || value === null) return value as any

if (isRec(value)) {
const res = {} as Rec
for (const k in value) res[k] = parseAtoms(ctx, value[k])
return res as any
}
if (cache.has(value)) return cache.get(value)

if (Array.isArray(value)) {
const res = []
for (const v of value) res.push(parseAtoms(ctx, v))
return res as any
}
let res: any = value

if (value instanceof Map) {
const res = new Map()
for (const [k, v] of value) res.set(k, parseAtoms(ctx, v))
return res as any
}

if (value instanceof Set) {
const res = new Set()
for (const v of value) res.add(parseAtoms(ctx, v))
return res as any
if (isRec(value)) {
res = {}
for (const k in value) res[k] = unwrap(ctx, value[k], cache)
} else if (Array.isArray(value)) {
res = []
for (const v of value) res.push(unwrap(ctx, v, cache))
} else if (value instanceof Map) {
res = new Map()
for (const [k, v] of value) res.set(k, unwrap(ctx, v, cache))
} else if (value instanceof Set) {
res = new Set()
for (const v of value) res.add(unwrap(ctx, v, cache))
}

return value as any
cache.set(value, res)
return res
}

0 comments on commit 5032ff1

Please sign in to comment.