import getComponentNameFromType from 'shared/getComponentNameFromType';
import {REACT_ELEMENT_TYPE} from 'shared/ReactSymbols';
import assign from 'shared/assign';
import hasOwnProperty from 'shared/hasOwnProperty';
import {checkKeyStringCoercion} from 'shared/CheckStringCoercion';
import ReactCurrentOwner from './ReactCurrentOwner';
let specialPropKeyWarningShown,
specialPropRefWarningShown,
didWarnAboutStringRefs;
if (__DEV__) {
didWarnAboutStringRefs = {};
}
function hasValidRef(config) {
if (__DEV__) {
if (hasOwnProperty.call(config, 'ref')) {
const getter = Object.getOwnPropertyDescriptor(config, 'ref').get;
if (getter && getter.isReactWarning) {
return false;
}
}
}
return config.ref !== undefined;
}
function hasValidKey(config) {
if (__DEV__) {
if (hasOwnProperty.call(config, 'key')) {
const getter = Object.getOwnPropertyDescriptor(config, 'key').get;
if (getter && getter.isReactWarning) {
return false;
}
}
}
return config.key !== undefined;
}
function defineKeyPropWarningGetter(props, displayName) {
const warnAboutAccessingKey = function () {
if (__DEV__) {
if (!specialPropKeyWarningShown) {
specialPropKeyWarningShown = true;
console.error(
'%s: `key` is not a prop. Trying to access it will result ' +
'in `undefined` being returned. If you need to access the same ' +
'value within the child component, you should pass it as a different ' +
'prop. (https://reactjs.org/link/special-props)',
displayName,
);
}
}
};
warnAboutAccessingKey.isReactWarning = true;
Object.defineProperty(props, 'key', {
get: warnAboutAccessingKey,
configurable: true,
});
}
function defineRefPropWarningGetter(props, displayName) {
const warnAboutAccessingRef = function () {
if (__DEV__) {
if (!specialPropRefWarningShown) {
specialPropRefWarningShown = true;
console.error(
'%s: `ref` is not a prop. Trying to access it will result ' +
'in `undefined` being returned. If you need to access the same ' +
'value within the child component, you should pass it as a different ' +
'prop. (https://reactjs.org/link/special-props)',
displayName,
);
}
}
};
warnAboutAccessingRef.isReactWarning = true;
Object.defineProperty(props, 'ref', {
get: warnAboutAccessingRef,
configurable: true,
});
}
function warnIfStringRefCannotBeAutoConverted(config) {
if (__DEV__) {
if (
typeof config.ref === 'string' &&
ReactCurrentOwner.current &&
config.__self &&
ReactCurrentOwner.current.stateNode !== config.__self
) {
const componentName = getComponentNameFromType(
ReactCurrentOwner.current.type,
);
if (!didWarnAboutStringRefs[componentName]) {
console.error(
'Component "%s" contains the string ref "%s". ' +
'Support for string refs will be removed in a future major release. ' +
'This case cannot be automatically converted to an arrow function. ' +
'We ask you to manually fix this case by using useRef() or createRef() instead. ' +
'Learn more about using refs safely here: ' +
'https://reactjs.org/link/strict-mode-string-ref',
componentName,
config.ref,
);
didWarnAboutStringRefs[componentName] = true;
}
}
}
}
function ReactElement(type, key, ref, owner, props) {
const element = {
$$typeof: REACT_ELEMENT_TYPE,
type: type,
key: key,
ref: ref,
props: props,
_owner: owner,
};
if (__DEV__) {
element._store = {};
Object.defineProperty(element._store, 'validated', {
configurable: false,
enumerable: false,
writable: true,
value: false,
});
Object.defineProperty(element, '_debugInfo', {
configurable: false,
enumerable: false,
writable: true,
value: null,
});
if (Object.freeze) {
Object.freeze(element.props);
Object.freeze(element);
}
}
return element;
}
export function createElement(type, config, children) {
let propName;
const props = {};
let key = null;
let ref = null;
if (config != null) {
if (hasValidRef(config)) {
ref = config.ref;
if (__DEV__) {
warnIfStringRefCannotBeAutoConverted(config);
}
}
if (hasValidKey(config)) {
if (__DEV__) {
checkKeyStringCoercion(config.key);
}
key = '' + config.key;
}
for (propName in config) {
if (
hasOwnProperty.call(config, propName) &&
propName !== 'key' &&
propName !== 'ref' &&
propName !== '__self' &&
propName !== '__source'
) {
props[propName] = config[propName];
}
}
}
const childrenLength = arguments.length - 2;
if (childrenLength === 1) {
props.children = children;
} else if (childrenLength > 1) {
const childArray = Array(childrenLength);
for (let i = 0; i < childrenLength; i++) {
childArray[i] = arguments[i + 2];
}
if (__DEV__) {
if (Object.freeze) {
Object.freeze(childArray);
}
}
props.children = childArray;
}
if (type && type.defaultProps) {
const defaultProps = type.defaultProps;
for (propName in defaultProps) {
if (props[propName] === undefined) {
props[propName] = defaultProps[propName];
}
}
}
if (__DEV__) {
if (key || ref) {
const displayName =
typeof type === 'function'
? type.displayName || type.name || 'Unknown'
: type;
if (key) {
defineKeyPropWarningGetter(props, displayName);
}
if (ref) {
defineRefPropWarningGetter(props, displayName);
}
}
}
return ReactElement(type, key, ref, ReactCurrentOwner.current, props);
}
export function createFactory(type) {
const factory = createElement.bind(null, type);
factory.type = type;
return factory;
}
export function cloneAndReplaceKey(oldElement, newKey) {
const newElement = ReactElement(
oldElement.type,
newKey,
oldElement.ref,
oldElement._owner,
oldElement.props,
);
return newElement;
}
export function cloneElement(element, config, children) {
if (element === null || element === undefined) {
throw new Error(
`React.cloneElement(...): The argument must be a React element, but you passed ${element}.`,
);
}
let propName;
const props = assign({}, element.props);
let key = element.key;
let ref = element.ref;
let owner = element._owner;
if (config != null) {
if (hasValidRef(config)) {
ref = config.ref;
owner = ReactCurrentOwner.current;
}
if (hasValidKey(config)) {
if (__DEV__) {
checkKeyStringCoercion(config.key);
}
key = '' + config.key;
}
let defaultProps;
if (element.type && element.type.defaultProps) {
defaultProps = element.type.defaultProps;
}
for (propName in config) {
if (
hasOwnProperty.call(config, propName) &&
propName !== 'key' &&
propName !== 'ref' &&
propName !== '__self' &&
propName !== '__source'
) {
if (config[propName] === undefined && defaultProps !== undefined) {
props[propName] = defaultProps[propName];
} else {
props[propName] = config[propName];
}
}
}
}
const childrenLength = arguments.length - 2;
if (childrenLength === 1) {
props.children = children;
} else if (childrenLength > 1) {
const childArray = Array(childrenLength);
for (let i = 0; i < childrenLength; i++) {
childArray[i] = arguments[i + 2];
}
props.children = childArray;
}
return ReactElement(element.type, key, ref, owner, props);
}
export function isValidElement(object) {
return (
typeof object === 'object' &&
object !== null &&
object.$$typeof === REACT_ELEMENT_TYPE
);
}