Given a serializer like this:
class Wrapper {
constructor(inner) {
this.inner = inner
}
}
const y = { hello: "world" }
const serialized = uneval({ x: new Wrapper(y), y }, (value, uneval) => {
if (value instanceof Wrapper) {
return `new Wrapper(${uneval(value.inner)
}
})
const deserialized = eval(s);
// deserialized.y !== deserialized.x.inner -- these should be the same, they have the same object identity on the input!
The reason this breaks, is because for custom serializers, uneval does not do a walk + stringify, but instead does a direct stringify. This means that inner values are not counted in counts, which causes them to not get deduped.
There is an API-compatibility preserving fix here, which is to call replacer twice (once to walk and once to actually stringify). In the first call to talk, the uneval function passed to it would be the reference walk. Based on whether walk was called or not, we can also skip the second call to the replacer for most scenarios.
I will work on a patch.
Given a serializer like this:
The reason this breaks, is because for custom serializers,
unevaldoes not do awalk+stringify, but instead does a directstringify. This means that inner values are not counted incounts, which causes them to not get deduped.There is an API-compatibility preserving fix here, which is to call
replacertwice (once to walk and once to actually stringify). In the first call to talk, theunevalfunction passed to it would be the referencewalk. Based on whetherwalkwas called or not, we can also skip the second call to the replacer for most scenarios.I will work on a patch.