packages/shared/util/Recoil_deepFreezeValue.js (56 lines of code) (raw):
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* Deep freeze values. Do not descend into React elements, Immutable structures,
* or in general things that respond poorly to being frozen. Follows the
* implementation of deepFreezeValue.
*
* @emails oncall+recoil
* @flow strict-local
* @format
*/
'use strict';
const {isReactNative, isWindow} = require('./Recoil_Environment');
const isNode = require('./Recoil_isNode');
const isPromise = require('./Recoil_isPromise');
function shouldNotBeFrozen(value: mixed): boolean {
// Primitives and functions:
if (value === null || typeof value !== 'object') {
return true;
}
// React elements:
switch (typeof value.$$typeof) {
case 'symbol':
return true;
case 'number':
return true;
}
// Immutable structures:
if (
value['@@__IMMUTABLE_ITERABLE__@@'] != null ||
value['@@__IMMUTABLE_KEYED__@@'] != null ||
value['@@__IMMUTABLE_INDEXED__@@'] != null ||
value['@@__IMMUTABLE_ORDERED__@@'] != null ||
value['@@__IMMUTABLE_RECORD__@@'] != null
) {
return true;
}
// DOM nodes:
if (isNode(value)) {
return true;
}
if (isPromise(value)) {
return true;
}
if (value instanceof Error) {
return true;
}
if (ArrayBuffer.isView(value)) {
return true;
}
// Some environments, just as Jest, don't work with the instanceof check
if (!isReactNative && isWindow(value)) {
return true;
}
return false;
}
// Recursively freeze a value to enforce it is read-only.
// This may also have minimal performance improvements for enumerating
// objects (based on browser implementations, of course)
function deepFreezeValue(value: mixed) {
if (typeof value !== 'object' || shouldNotBeFrozen(value)) {
return;
}
Object.freeze(value); // Make all properties read-only
for (const key in value) {
// $FlowFixMe[method-unbinding] added when improving typing for this parameters
if (Object.prototype.hasOwnProperty.call(value, key)) {
const prop = value[key];
// Prevent infinite recurssion for circular references.
if (typeof prop === 'object' && prop != null && !Object.isFrozen(prop)) {
deepFreezeValue(prop);
}
}
}
Object.seal(value); // This also makes existing properties non-configurable.
}
module.exports = deepFreezeValue;