Add comments to cloneDeepSerializable()

https://github.com/theatre-js/theatre/pull/118#discussion_r846635429
This commit is contained in:
Aria Minaei 2022-04-10 20:36:10 +02:00
parent 679629def8
commit a5cf72cd57
2 changed files with 18 additions and 5 deletions

View file

@ -609,6 +609,11 @@ export interface IBasePropType<
* Each prop config has a `deserializeAndSanitize()` function that deserializes and sanitizes * Each prop config has a `deserializeAndSanitize()` function that deserializes and sanitizes
* any js value into one that is acceptable by this prop config, or `undefined`. * any js value into one that is acceptable by this prop config, or `undefined`.
* *
* As a rule, the value returned by this function should not hold any reference to `json` or any
* other value referenced by the descendent props of `json`. This is to ensure that json values
* controlled by the user can never change the values in the store. See `deserializeAndSanitize()` in
* `t.compound()` or `t.rgba()` as examples.
*
* The `DeserializeType` is usually equal to `ValueType`. That is the case with * The `DeserializeType` is usually equal to `ValueType`. That is the case with
* all simple prop configs, such as `number`, `string`, or `rgba`. However, composite * all simple prop configs, such as `number`, `string`, or `rgba`. However, composite
* configs such as `compound` or `enum` may deserialize+sanitize into a partial value. For example, * configs such as `compound` or `enum` may deserialize+sanitize into a partial value. For example,

View file

@ -17,7 +17,15 @@ import {getPropConfigByPath} from '@theatre/shared/propTypes/utils'
import {isPlainObject} from 'lodash-es' import {isPlainObject} from 'lodash-es'
import userReadableTypeOfValue from '@theatre/shared/utils/userReadableTypeOfValue' import userReadableTypeOfValue from '@theatre/shared/utils/userReadableTypeOfValue'
function cloneDeepSerializable<T>(v: T): T | undefined { /**
* Deep-clones a plain JS object or a `string | number | boolean`. In case of a plain
* object, all its sub-props that aren't `string | number | boolean` get pruned. Also,
* all empty objects (i.e. `{}`) get pruned.
*
* This is only used by {@link ITransactionPrivateApi.set} and it follows the global rule
* that values pointed to by `object.props[...]` are never `null | undefined` or an empty object.
*/
function cloneDeepSerializableAndPrune<T>(v: T): T | undefined {
if ( if (
typeof v === 'boolean' || typeof v === 'boolean' ||
typeof v === 'string' || typeof v === 'string' ||
@ -28,8 +36,8 @@ function cloneDeepSerializable<T>(v: T): T | undefined {
const cloned: $IntentionalAny = {} const cloned: $IntentionalAny = {}
let clonedAtLeastOneProp = false let clonedAtLeastOneProp = false
for (const [key, val] of Object.entries(v)) { for (const [key, val] of Object.entries(v)) {
const clonedVal = cloneDeepSerializable(val) const clonedVal = cloneDeepSerializableAndPrune(val)
if (typeof clonedVal !== 'undefined') { if (clonedVal !== undefined) {
cloned[key] = val cloned[key] = val
clonedAtLeastOneProp = true clonedAtLeastOneProp = true
} }
@ -69,7 +77,7 @@ export default function createTransactionPrivateApi(
return { return {
set: (pointer, value) => { set: (pointer, value) => {
ensureRunning() ensureRunning()
const _value = cloneDeepSerializable(value) const _value = cloneDeepSerializableAndPrune(value)
if (typeof _value === 'undefined') return if (typeof _value === 'undefined') return
const {root, path} = getPointerParts(pointer as Pointer<$FixMe>) const {root, path} = getPointerParts(pointer as Pointer<$FixMe>)
@ -104,7 +112,7 @@ export default function createTransactionPrivateApi(
return return
} }
const deserialized = cloneDeepSerializable( const deserialized = cloneDeepSerializableAndPrune(
propConfig.deserializeAndSanitize(value), propConfig.deserializeAndSanitize(value),
) )
if (deserialized === undefined) { if (deserialized === undefined) {