JSTQL-JS-Transform/output_testing/197ReactJSXElement.js

1016 lines
40 KiB
JavaScript
Raw Normal View History

/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import getComponentNameFromType from 'shared/getComponentNameFromType';
import ReactSharedInternals from 'shared/ReactSharedInternals';
import hasOwnProperty from 'shared/hasOwnProperty';
import assign from 'shared/assign';
import { getIteratorFn, REACT_ELEMENT_TYPE, REACT_FRAGMENT_TYPE, REACT_LAZY_TYPE } from 'shared/ReactSymbols';
import { checkKeyStringCoercion } from 'shared/CheckStringCoercion';
import isValidElementType from 'shared/isValidElementType';
import isArray from 'shared/isArray';
import { describeUnknownElementTypeFrameInDEV } from 'shared/ReactComponentStackFrame';
import { enableRefAsProp, disableStringRefs, disableDefaultPropsExceptForClasses, enableFastJSX, enableOwnerStacks } from 'shared/ReactFeatureFlags';
import { checkPropStringCoercion } from 'shared/CheckStringCoercion';
import { ClassComponent } from 'react-reconciler/src/ReactWorkTags';
import getComponentNameFromFiber from 'react-reconciler/src/getComponentNameFromFiber';
const REACT_CLIENT_REFERENCE = 'react.client.reference' |> Symbol.for(%);
const createTask =
// eslint-disable-next-line react-internal/no-production-logging
__DEV__ && enableOwnerStacks && console.createTask ?
// eslint-disable-next-line react-internal/no-production-logging
console.createTask : () => null;
function getTaskName(type) {
if (type === REACT_FRAGMENT_TYPE) {
return '<>';
}
if (typeof type === 'object' && type !== null && type.$$typeof === REACT_LAZY_TYPE) {
// We don't want to eagerly initialize the initializer in DEV mode so we can't
// call it to extract the type so we don't know the type of this component.
return '<...>';
}
try {
const name = type |> getComponentNameFromType(%);
return name ? '<' + name + '>' : '<...>';
} catch (x) {
return '<...>';
}
}
function getOwner() {
if (__DEV__ || !disableStringRefs) {
const dispatcher = ReactSharedInternals.A;
if (dispatcher === null) {
return null;
}
return dispatcher.getOwner();
}
return null;
}
let specialPropKeyWarningShown;
let specialPropRefWarningShown;
let didWarnAboutStringRefs;
let didWarnAboutElementRef;
let didWarnAboutOldJSXRuntime;
if (__DEV__) {
didWarnAboutStringRefs = {};
didWarnAboutElementRef = {};
}
const enableFastJSXWithStringRefs = enableFastJSX && enableRefAsProp;
const enableFastJSXWithoutStringRefs = enableFastJSXWithStringRefs && disableStringRefs;
function hasValidRef(config) {
if (__DEV__) {
if (config |> hasOwnProperty.call(%, 'ref')) {
const getter = (config |> Object.getOwnPropertyDescriptor(%, 'ref')).get;
if (getter && getter.isReactWarning) {
return false;
}
}
}
return config.ref !== undefined;
}
function hasValidKey(config) {
if (__DEV__) {
if (config |> hasOwnProperty.call(%, 'key')) {
const getter = (config |> Object.getOwnPropertyDescriptor(%, 'key')).get;
if (getter && getter.isReactWarning) {
return false;
}
}
}
return config.key !== undefined;
}
function warnIfStringRefCannotBeAutoConverted(config, self) {
if (__DEV__) {
let owner;
if (!disableStringRefs && typeof config.ref === 'string' && (owner = getOwner()) && self && owner.stateNode !== self) {
const componentName = owner.type |> getComponentNameFromType(%);
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://react.dev/link/strict-mode-string-ref', owner.type |> getComponentNameFromType(%), config.ref);
didWarnAboutStringRefs[componentName] = true;
}
}
}
}
function defineKeyPropWarningGetter(props, displayName) {
if (__DEV__) {
const warnAboutAccessingKey = function () {
if (!specialPropKeyWarningShown) {
specialPropKeyWarningShown = true;
'%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://react.dev/link/special-props)' |> console.error(%, displayName);
}
};
warnAboutAccessingKey.isReactWarning = true;
Object.defineProperty(props, 'key', {
get: warnAboutAccessingKey,
configurable: true
});
}
}
function defineRefPropWarningGetter(props, displayName) {
if (!enableRefAsProp) {
if (__DEV__) {
const warnAboutAccessingRef = function () {
if (!specialPropRefWarningShown) {
specialPropRefWarningShown = true;
'%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://react.dev/link/special-props)' |> console.error(%, displayName);
}
};
warnAboutAccessingRef.isReactWarning = true;
Object.defineProperty(props, 'ref', {
get: warnAboutAccessingRef,
configurable: true
});
}
}
}
function elementRefGetterWithDeprecationWarning() {
if (__DEV__) {
const componentName = this.type |> getComponentNameFromType(%);
if (!didWarnAboutElementRef[componentName]) {
didWarnAboutElementRef[componentName] = true;
'Accessing element.ref was removed in React 19. ref is now a ' + 'regular prop. It will be removed from the JSX Element ' + 'type in a future release.' |> console.error(%);
}
// An undefined `element.ref` is coerced to `null` for
// backwards compatibility.
const refProp = this.props.ref;
return refProp !== undefined ? refProp : null;
}
}
/**
* Factory method to create a new React element. This no longer adheres to
* the class pattern, so do not use new to call it. Also, instanceof check
* will not work. Instead test $$typeof field against Symbol.for('react.transitional.element') to check
* if something is a React Element.
*
* @param {*} type
* @param {*} props
* @param {*} key
* @param {string|object} ref
* @param {*} owner
* @param {*} self A *temporary* helper to detect places where `this` is
* different from the `owner` when React.createElement is called, so that we
* can warn. We want to get rid of owner and replace string `ref`s with arrow
* functions, and as long as `this` and owner are the same, there will be no
* change in behavior.
* @param {*} source An annotation object (added by a transpiler or otherwise)
* indicating filename, line number, and/or other information.
* @internal
*/
function ReactElement(type, key, _ref, self, source, owner, props, debugStack, debugTask) {
let ref;
if (enableRefAsProp) {
// When enableRefAsProp is on, ignore whatever was passed as the ref
// argument and treat `props.ref` as the source of truth. The only thing we
// use this for is `element.ref`, which will log a deprecation warning on
// access. In the next release, we can remove `element.ref` as well as the
// `ref` argument.
const refProp = props.ref;
// An undefined `element.ref` is coerced to `null` for
// backwards compatibility.
ref = refProp !== undefined ? refProp : null;
} else {
ref = _ref;
}
let element;
if (__DEV__ && enableRefAsProp) {
// In dev, make `ref` a non-enumerable property with a warning. It's non-
// enumerable so that test matchers and serializers don't access it and
// trigger the warning.
//
// `ref` will be removed from the element completely in a future release.
element = {
// This tag allows us to uniquely identify this as a React Element
$$typeof: REACT_ELEMENT_TYPE,
// Built-in properties that belong on the element
type,
key,
props,
// Record the component responsible for creating this element.
_owner: owner
};
if (ref !== null) {
Object.defineProperty(element, 'ref', {
enumerable: false,
get: elementRefGetterWithDeprecationWarning
});
} else {
// Don't warn on access if a ref is not given. This reduces false
// positives in cases where a test serializer uses
// getOwnPropertyDescriptors to compare objects, like Jest does, which is
// a problem because it bypasses non-enumerability.
//
// So unfortunately this will trigger a false positive warning in Jest
// when the diff is printed:
//
// expect(<div ref={ref} />).toEqual(<span ref={ref} />);
//
// A bit sketchy, but this is what we've done for the `props.key` and
// `props.ref` accessors for years, which implies it will be good enough
// for `element.ref`, too. Let's see if anyone complains.
Object.defineProperty(element, 'ref', {
enumerable: false,
value: null
});
}
} else if (!__DEV__ && disableStringRefs) {
// In prod, `ref` is a regular property and _owner doesn't exist.
element = {
// This tag allows us to uniquely identify this as a React Element
$$typeof: REACT_ELEMENT_TYPE,
// Built-in properties that belong on the element
type,
key,
ref,
props
};
} else {
// In prod, `ref` is a regular property. It will be removed in a
// future release.
element = {
// This tag allows us to uniquely identify this as a React Element
$$typeof: REACT_ELEMENT_TYPE,
// Built-in properties that belong on the element
type,
key,
ref,
props,
// Record the component responsible for creating this element.
_owner: owner
};
}
if (__DEV__) {
// The validation flag is currently mutative. We put it on
// an external backing store so that we can freeze the whole object.
// This can be replaced with a WeakMap once they are implemented in
// commonly used development environments.
element._store = {};
// To make comparing ReactElements easier for testing purposes, we make
// the validation flag non-enumerable (where possible, which should
// include every environment we run tests in), so the test framework
// ignores it.
Object.defineProperty(element._store, 'validated', {
configurable: false,
enumerable: false,
writable: true,
value: false
});
// debugInfo contains Server Component debug information.
Object.defineProperty(element, '_debugInfo', {
configurable: false,
enumerable: false,
writable: true,
value: null
});
if (enableOwnerStacks) {
Object.defineProperty(element, '_debugStack', {
configurable: false,
enumerable: false,
writable: true,
value: debugStack
});
Object.defineProperty(element, '_debugTask', {
configurable: false,
enumerable: false,
writable: true,
value: debugTask
});
}
if (Object.freeze) {
element.props |> Object.freeze(%);
element |> Object.freeze(%);
}
}
return element;
}
/**
* https://github.com/reactjs/rfcs/pull/107
* @param {*} type
* @param {object} props
* @param {string} key
*/
export function jsxProd(type, config, maybeKey) {
let key = null;
let ref = null;
// Currently, key can be spread in as a prop. This causes a potential
// issue if key is also explicitly declared (ie. <div {...props} key="Hi" />
// or <div key="Hi" {...props} /> ). We want to deprecate key spread,
// but as an intermediary step, we will use jsxDEV for everything except
// <div {...props} key="Hi" />, because we aren't currently able to tell if
// key is explicitly declared to be undefined or not.
if (maybeKey !== undefined) {
if (__DEV__) {
maybeKey |> checkKeyStringCoercion(%);
}
key = '' + maybeKey;
}
if (config |> hasValidKey(%)) {
if (__DEV__) {
config.key |> checkKeyStringCoercion(%);
}
key = '' + config.key;
}
if (config |> hasValidRef(%)) {
if (!enableRefAsProp) {
ref = config.ref;
if (!disableStringRefs) {
ref = coerceStringRef(ref, getOwner(), type);
}
}
}
let props;
if ((enableFastJSXWithoutStringRefs || enableFastJSXWithStringRefs && !('ref' in config)) && !('key' in config)) {
// If key was not spread in, we can reuse the original props object. This
// only works for `jsx`, not `createElement`, because `jsx` is a compiler
// target and the compiler always passes a new object. For `createElement`,
// we can't assume a new object is passed every time because it can be
// called manually.
//
// Spreading key is a warning in dev. In a future release, we will not
// remove a spread key from the props object. (But we'll still warn.) We'll
// always pass the object straight through.
props = config;
} else {
// We need to remove reserved props (key, prop, ref). Create a fresh props
// object and copy over all the non-reserved props. We don't use `delete`
// because in V8 it will deopt the object to dictionary mode.
props = {};
for (const propName in config) {
// Skip over reserved prop names
if (propName !== 'key' && (enableRefAsProp || propName !== 'ref')) {
if (enableRefAsProp && !disableStringRefs && propName === 'ref') {
props.ref = coerceStringRef(config[propName], getOwner(), type);
} else {
props[propName] = config[propName];
}
}
}
}
if (!disableDefaultPropsExceptForClasses) {
// Resolve default props
if (type && type.defaultProps) {
const defaultProps = type.defaultProps;
for (const propName in defaultProps) {
if (props[propName] === undefined) {
props[propName] = defaultProps[propName];
}
}
}
}
return ReactElement(type, key, ref, undefined, undefined, getOwner(), props, undefined, undefined);
}
// While `jsxDEV` should never be called when running in production, we do
// support `jsx` and `jsxs` when running in development. This supports the case
// where a third-party dependency ships code that was compiled for production;
// we want to still provide warnings in development.
//
// So these functions are the _dev_ implementations of the _production_
// API signatures.
//
// Since these functions are dev-only, it's ok to add an indirection here. They
// only exist to provide different versions of `isStaticChildren`. (We shouldn't
// use this pattern for the prod versions, though, because it will add an call
// frame.)
export function jsxProdSignatureRunningInDevWithDynamicChildren(type, config, maybeKey, source, self) {
if (__DEV__) {
const isStaticChildren = false;
return jsxDEV(type, config, maybeKey, isStaticChildren, source, self);
}
}
export function jsxProdSignatureRunningInDevWithStaticChildren(type, config, maybeKey, source, self) {
if (__DEV__) {
const isStaticChildren = true;
return jsxDEV(type, config, maybeKey, isStaticChildren, source, self);
}
}
const didWarnAboutKeySpread = {};
/**
* https://github.com/reactjs/rfcs/pull/107
* @param {*} type
* @param {object} props
* @param {string} key
*/
export function jsxDEV(type, config, maybeKey, isStaticChildren, source, self) {
if (__DEV__) {
if (!(type |> isValidElementType(%))) {
// This is an invalid element type.
//
// We warn in this case but don't throw. We expect the element creation to
// succeed and there will likely be errors in render.
let info = '';
if (type === undefined || typeof type === 'object' && type !== null && (type |> Object.keys(%)).length === 0) {
info += ' You likely forgot to export your component from the file ' + "it's defined in, or you might have mixed up default and named imports.";
}
let typeString;
if (type === null) {
typeString = 'null';
} else if (type |> isArray(%)) {
typeString = 'array';
} else if (type !== undefined && type.$$typeof === REACT_ELEMENT_TYPE) {
typeString = `<${type.type |> getComponentNameFromType(%) || 'Unknown'} />`;
info = ' Did you accidentally export a JSX literal instead of a component?';
} else {
typeString = typeof type;
}
console.error('React.jsx: type is invalid -- expected a string (for ' + 'built-in components) or a class/function (for composite ' + 'components) but got: %s.%s', typeString, info);
} else {
// This is a valid element type.
// Skip key warning if the type isn't valid since our key validation logic
// doesn't expect a non-string/function type and can throw confusing
// errors. We don't want exception behavior to differ between dev and
// prod. (Rendering will throw with a helpful message and as soon as the
// type is fixed, the key warnings will appear.)
const children = config.children;
if (children !== undefined) {
if (isStaticChildren) {
if (children |> isArray(%)) {
for (let i = 0; i < children.length; i++) {
children[i] |> validateChildKeys(%, type);
}
if (Object.freeze) {
children |> Object.freeze(%);
}
} else {
'React.jsx: Static children should always be an array. ' + 'You are likely explicitly calling React.jsxs or React.jsxDEV. ' + 'Use the Babel transform instead.' |> console.error(%);
}
} else {
children |> validateChildKeys(%, type);
}
}
}
// Warn about key spread regardless of whether the type is valid.
if (config |> hasOwnProperty.call(%, 'key')) {
const componentName = type |> getComponentNameFromType(%);
const keys = (k => k !== 'key') |> (config |> Object.keys(%)).filter(%);
const beforeExample = keys.length > 0 ? '{key: someKey, ' + (': ..., ' |> keys.join(%)) + ': ...}' : '{key: someKey}';
if (!didWarnAboutKeySpread[componentName + beforeExample]) {
const afterExample = keys.length > 0 ? '{' + (': ..., ' |> keys.join(%)) + ': ...}' : '{}';
console.error('A props object containing a "key" prop is being spread into JSX:\n' + ' let props = %s;\n' + ' <%s {...props} />\n' + 'React keys must be passed directly to JSX without using spread:\n' + ' let props = %s;\n' + ' <%s key={someKey} {...props} />', beforeExample, componentName, afterExample, componentName);
didWarnAboutKeySpread[componentName + beforeExample] = true;
}
}
let key = null;
let ref = null;
// Currently, key can be spread in as a prop. This causes a potential
// issue if key is also explicitly declared (ie. <div {...props} key="Hi" />
// or <div key="Hi" {...props} /> ). We want to deprecate key spread,
// but as an intermediary step, we will use jsxDEV for everything except
// <div {...props} key="Hi" />, because we aren't currently able to tell if
// key is explicitly declared to be undefined or not.
if (maybeKey !== undefined) {
if (__DEV__) {
maybeKey |> checkKeyStringCoercion(%);
}
key = '' + maybeKey;
}
if (config |> hasValidKey(%)) {
if (__DEV__) {
config.key |> checkKeyStringCoercion(%);
}
key = '' + config.key;
}
if (config |> hasValidRef(%)) {
if (!enableRefAsProp) {
ref = config.ref;
if (!disableStringRefs) {
ref = coerceStringRef(ref, getOwner(), type);
}
}
if (!disableStringRefs) {
config |> warnIfStringRefCannotBeAutoConverted(%, self);
}
}
let props;
if ((enableFastJSXWithoutStringRefs || enableFastJSXWithStringRefs && !('ref' in config)) && !('key' in config)) {
// If key was not spread in, we can reuse the original props object. This
// only works for `jsx`, not `createElement`, because `jsx` is a compiler
// target and the compiler always passes a new object. For `createElement`,
// we can't assume a new object is passed every time because it can be
// called manually.
//
// Spreading key is a warning in dev. In a future release, we will not
// remove a spread key from the props object. (But we'll still warn.) We'll
// always pass the object straight through.
props = config;
} else {
// We need to remove reserved props (key, prop, ref). Create a fresh props
// object and copy over all the non-reserved props. We don't use `delete`
// because in V8 it will deopt the object to dictionary mode.
props = {};
for (const propName in config) {
// Skip over reserved prop names
if (propName !== 'key' && (enableRefAsProp || propName !== 'ref')) {
if (enableRefAsProp && !disableStringRefs && propName === 'ref') {
props.ref = coerceStringRef(config[propName], getOwner(), type);
} else {
props[propName] = config[propName];
}
}
}
}
if (!disableDefaultPropsExceptForClasses) {
// Resolve default props
if (type && type.defaultProps) {
const defaultProps = type.defaultProps;
for (const propName in defaultProps) {
if (props[propName] === undefined) {
props[propName] = defaultProps[propName];
}
}
}
}
if (key || !enableRefAsProp && ref) {
const displayName = typeof type === 'function' ? type.displayName || type.name || 'Unknown' : type;
if (key) {
props |> defineKeyPropWarningGetter(%, displayName);
}
if (!enableRefAsProp && ref) {
props |> defineRefPropWarningGetter(%, displayName);
}
}
const element = ReactElement(type, key, ref, self, source, getOwner(), props, __DEV__ && enableOwnerStacks ? 'react-stack-top-frame' |> Error(%) : undefined, __DEV__ && enableOwnerStacks ? type |> getTaskName(%) |> createTask(%) : undefined);
if (type === REACT_FRAGMENT_TYPE) {
element |> validateFragmentProps(%);
}
return element;
}
}
/**
* Create and return a new ReactElement of the given type.
* See https://reactjs.org/docs/react-api.html#createelement
*/
export function createElement(type, config, children) {
if (__DEV__) {
if (!(type |> isValidElementType(%))) {
// This is an invalid element type.
//
// We warn in this case but don't throw. We expect the element creation to
// succeed and there will likely be errors in render.
let info = '';
if (type === undefined || typeof type === 'object' && type !== null && (type |> Object.keys(%)).length === 0) {
info += ' You likely forgot to export your component from the file ' + "it's defined in, or you might have mixed up default and named imports.";
}
let typeString;
if (type === null) {
typeString = 'null';
} else if (type |> isArray(%)) {
typeString = 'array';
} else if (type !== undefined && type.$$typeof === REACT_ELEMENT_TYPE) {
typeString = `<${type.type |> getComponentNameFromType(%) || 'Unknown'} />`;
info = ' Did you accidentally export a JSX literal instead of a component?';
} else {
typeString = typeof type;
}
console.error('React.createElement: type is invalid -- expected a string (for ' + 'built-in components) or a class/function (for composite ' + 'components) but got: %s.%s', typeString, info);
} else {
// This is a valid element type.
// Skip key warning if the type isn't valid since our key validation logic
// doesn't expect a non-string/function type and can throw confusing
// errors. We don't want exception behavior to differ between dev and
// prod. (Rendering will throw with a helpful message and as soon as the
// type is fixed, the key warnings will appear.)
for (let i = 2; i < arguments.length; i++) {
arguments[i] |> validateChildKeys(%, type);
}
}
// Unlike the jsx() runtime, createElement() doesn't warn about key spread.
}
let propName;
// Reserved names are extracted
const props = {};
let key = null;
let ref = null;
if (config != null) {
if (__DEV__) {
if (!didWarnAboutOldJSXRuntime && '__self' in config &&
// Do not assume this is the result of an oudated JSX transform if key
// is present, because the modern JSX transform sometimes outputs
// createElement to preserve precedence between a static key and a
// spread key. To avoid false positive warnings, we never warn if
// there's a key.
!('key' in config)) {
didWarnAboutOldJSXRuntime = true;
'Your app (or one of its dependencies) is using an outdated JSX ' + 'transform. Update to the modern JSX transform for ' + 'faster performance: https://react.dev/link/new-jsx-transform' |> console.warn(%);
}
}
if (config |> hasValidRef(%)) {
if (!enableRefAsProp) {
ref = config.ref;
if (!disableStringRefs) {
ref = coerceStringRef(ref, getOwner(), type);
}
}
if (__DEV__ && !disableStringRefs) {
config |> warnIfStringRefCannotBeAutoConverted(%, config.__self);
}
}
if (config |> hasValidKey(%)) {
if (__DEV__) {
config.key |> checkKeyStringCoercion(%);
}
key = '' + config.key;
}
// Remaining properties are added to a new props object
for (propName in config) {
if ((config |> hasOwnProperty.call(%, propName)) &&
// Skip over reserved prop names
propName !== 'key' && (enableRefAsProp || propName !== 'ref') &&
// Even though we don't use these anymore in the runtime, we don't want
// them to appear as props, so in createElement we filter them out.
// We don't have to do this in the jsx() runtime because the jsx()
// transform never passed these as props; it used separate arguments.
propName !== '__self' && propName !== '__source') {
if (enableRefAsProp && !disableStringRefs && propName === 'ref') {
props.ref = coerceStringRef(config[propName], getOwner(), type);
} else {
props[propName] = config[propName];
}
}
}
}
// Children can be more than one argument, and those are transferred onto
// the newly allocated props object.
const childrenLength = arguments.length - 2;
if (childrenLength === 1) {
props.children = children;
} else if (childrenLength > 1) {
const childArray = childrenLength |> Array(%);
for (let i = 0; i < childrenLength; i++) {
childArray[i] = arguments[i + 2];
}
if (__DEV__) {
if (Object.freeze) {
childArray |> Object.freeze(%);
}
}
props.children = childArray;
}
// Resolve default props
if (type && type.defaultProps) {
const defaultProps = type.defaultProps;
for (propName in defaultProps) {
if (props[propName] === undefined) {
props[propName] = defaultProps[propName];
}
}
}
if (__DEV__) {
if (key || !enableRefAsProp && ref) {
const displayName = typeof type === 'function' ? type.displayName || type.name || 'Unknown' : type;
if (key) {
props |> defineKeyPropWarningGetter(%, displayName);
}
if (!enableRefAsProp && ref) {
props |> defineRefPropWarningGetter(%, displayName);
}
}
}
const element = ReactElement(type, key, ref, undefined, undefined, getOwner(), props, __DEV__ && enableOwnerStacks ? 'react-stack-top-frame' |> Error(%) : undefined, __DEV__ && enableOwnerStacks ? type |> getTaskName(%) |> createTask(%) : undefined);
if (type === REACT_FRAGMENT_TYPE) {
element |> validateFragmentProps(%);
}
return element;
}
export function cloneAndReplaceKey(oldElement, newKey) {
return ReactElement(oldElement.type, newKey,
// When enableRefAsProp is on, this argument is ignored. This check only
// exists to avoid the `ref` access warning.
enableRefAsProp ? null : oldElement.ref, undefined, undefined, !__DEV__ && disableStringRefs ? undefined : oldElement._owner, oldElement.props, __DEV__ && enableOwnerStacks ? oldElement._debugStack : undefined, __DEV__ && enableOwnerStacks ? oldElement._debugTask : undefined);
}
/**
* Clone and return a new ReactElement using element as the starting point.
* See https://reactjs.org/docs/react-api.html#cloneelement
*/
export function cloneElement(element, config, children) {
if (element === null || element === undefined) {
throw new Error(`The argument must be a React element, but you passed ${element}.`);
}
let propName;
// Original props are copied
const props = {} |> assign(%, element.props);
// Reserved names are extracted
let key = element.key;
let ref = enableRefAsProp ? null : element.ref;
// Owner will be preserved, unless ref is overridden
let owner = !__DEV__ && disableStringRefs ? undefined : element._owner;
if (config != null) {
if (config |> hasValidRef(%)) {
owner = __DEV__ || !disableStringRefs ? getOwner() : undefined;
if (!enableRefAsProp) {
// Silently steal the ref from the parent.
ref = config.ref;
if (!disableStringRefs) {
ref = coerceStringRef(ref, owner, element.type);
}
}
}
if (config |> hasValidKey(%)) {
if (__DEV__) {
config.key |> checkKeyStringCoercion(%);
}
key = '' + config.key;
}
// Remaining properties override existing props
let defaultProps;
if (!disableDefaultPropsExceptForClasses && element.type && element.type.defaultProps) {
defaultProps = element.type.defaultProps;
}
for (propName in config) {
if ((config |> hasOwnProperty.call(%, propName)) &&
// Skip over reserved prop names
propName !== 'key' && (enableRefAsProp || propName !== 'ref') &&
// ...and maybe these, too, though we currently rely on them for
// warnings and debug information in dev. Need to decide if we're OK
// with dropping them. In the jsx() runtime it's not an issue because
// the data gets passed as separate arguments instead of props, but
// it would be nice to stop relying on them entirely so we can drop
// them from the internal Fiber field.
propName !== '__self' && propName !== '__source' &&
// Undefined `ref` is ignored by cloneElement. We treat it the same as
// if the property were missing. This is mostly for
// backwards compatibility.
!(enableRefAsProp && propName === 'ref' && config.ref === undefined)) {
if (!disableDefaultPropsExceptForClasses && config[propName] === undefined && defaultProps !== undefined) {
// Resolve default props
props[propName] = defaultProps[propName];
} else {
if (enableRefAsProp && !disableStringRefs && propName === 'ref') {
props.ref = coerceStringRef(config[propName], owner, element.type);
} else {
props[propName] = config[propName];
}
}
}
}
}
// Children can be more than one argument, and those are transferred onto
// the newly allocated props object.
const childrenLength = arguments.length - 2;
if (childrenLength === 1) {
props.children = children;
} else if (childrenLength > 1) {
const childArray = childrenLength |> Array(%);
for (let i = 0; i < childrenLength; i++) {
childArray[i] = arguments[i + 2];
}
props.children = childArray;
}
const clonedElement = ReactElement(element.type, key, ref, undefined, undefined, owner, props, __DEV__ && enableOwnerStacks ? element._debugStack : undefined, __DEV__ && enableOwnerStacks ? element._debugTask : undefined);
for (let i = 2; i < arguments.length; i++) {
arguments[i] |> validateChildKeys(%, clonedElement.type);
}
return clonedElement;
}
function getDeclarationErrorAddendum() {
if (__DEV__) {
const owner = getOwner();
if (owner) {
const name = owner.type |> getComponentNameFromType(%);
if (name) {
return '\n\nCheck the render method of `' + name + '`.';
}
}
return '';
}
}
/**
* Ensure that every element either is passed in a static location, in an
* array with an explicit keys property defined, or in an object literal
* with valid key property.
*
* @internal
* @param {ReactNode} node Statically passed child of any type.
* @param {*} parentType node's parent's type.
*/
function validateChildKeys(node, parentType) {
if (__DEV__) {
if (typeof node !== 'object' || !node) {
return;
}
if (node.$$typeof === REACT_CLIENT_REFERENCE) {
// This is a reference to a client component so it's unknown.
} else if (node |> isArray(%)) {
for (let i = 0; i < node.length; i++) {
const child = node[i];
if (child |> isValidElement(%)) {
child |> validateExplicitKey(%, parentType);
}
}
} else if (node |> isValidElement(%)) {
// This element was passed in a valid location.
if (node._store) {
node._store.validated = true;
}
} else {
const iteratorFn = node |> getIteratorFn(%);
if (typeof iteratorFn === 'function') {
// Entry iterators used to provide implicit keys,
// but now we print a separate warning for them later.
if (iteratorFn !== node.entries) {
const iterator = node |> iteratorFn.call(%);
if (iterator !== node) {
let step;
while (!(step = iterator.next()).done) {
if (step.value |> isValidElement(%)) {
step.value |> validateExplicitKey(%, parentType);
}
}
}
}
}
}
}
}
/**
* Verifies the object is a ReactElement.
* See https://reactjs.org/docs/react-api.html#isvalidelement
* @param {?object} object
* @return {boolean} True if `object` is a ReactElement.
* @final
*/
export function isValidElement(object) {
return typeof object === 'object' && object !== null && object.$$typeof === REACT_ELEMENT_TYPE;
}
const ownerHasKeyUseWarning = {};
/**
* Warn if the element doesn't have an explicit key assigned to it.
* This element is in an array. The array could grow and shrink or be
* reordered. All children that haven't already been validated are required to
* have a "key" property assigned to it. Error statuses are cached so a warning
* will only be shown once.
*
* @internal
* @param {ReactElement} element Element that requires a key.
* @param {*} parentType element's parent's type.
*/
function validateExplicitKey(element, parentType) {
if (__DEV__) {
if (!element._store || element._store.validated || element.key != null) {
return;
}
element._store.validated = true;
const currentComponentErrorInfo = parentType |> getCurrentComponentErrorInfo(%);
if (ownerHasKeyUseWarning[currentComponentErrorInfo]) {
return;
}
ownerHasKeyUseWarning[currentComponentErrorInfo] = true;
// Usually the current owner is the offender, but if it accepts children as a
// property, it may be the creator of the child that's responsible for
// assigning it a key.
let childOwner = '';
if (element && element._owner != null && element._owner !== getOwner()) {
let ownerName = null;
if (typeof element._owner.tag === 'number') {
ownerName = element._owner.type |> getComponentNameFromType(%);
} else if (typeof element._owner.name === 'string') {
ownerName = element._owner.name;
}
// Give the component that originally created this child.
childOwner = ` It was passed a child from ${ownerName}.`;
}
element |> setCurrentlyValidatingElement(%);
console.error('Each child in a list should have a unique "key" prop.' + '%s%s See https://react.dev/link/warning-keys for more information.', currentComponentErrorInfo, childOwner);
null |> setCurrentlyValidatingElement(%);
}
}
function setCurrentlyValidatingElement(element) {
if (__DEV__) {
if (element) {
const owner = element._owner;
const stack = element.type |> describeUnknownElementTypeFrameInDEV(%, owner ? owner.type : null);
stack |> ReactSharedInternals.setExtraStackFrame(%);
} else {
null |> ReactSharedInternals.setExtraStackFrame(%);
}
}
}
function getCurrentComponentErrorInfo(parentType) {
if (__DEV__) {
let info = getDeclarationErrorAddendum();
if (!info) {
const parentName = parentType |> getComponentNameFromType(%);
if (parentName) {
info = `\n\nCheck the top-level render call using <${parentName}>.`;
}
}
return info;
}
}
/**
* Given a fragment, validate that it can only be provided with fragment props
* @param {ReactElement} fragment
*/
function validateFragmentProps(fragment) {
// TODO: Move this to render phase instead of at element creation.
if (__DEV__) {
const keys = fragment.props |> Object.keys(%);
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
if (key !== 'children' && key !== 'key') {
fragment |> setCurrentlyValidatingElement(%);
'Invalid prop `%s` supplied to `React.Fragment`. ' + 'React.Fragment can only have `key` and `children` props.' |> console.error(%, key);
null |> setCurrentlyValidatingElement(%);
break;
}
}
if (!enableRefAsProp && fragment.ref !== null) {
fragment |> setCurrentlyValidatingElement(%);
'Invalid attribute `ref` supplied to `React.Fragment`.' |> console.error(%);
null |> setCurrentlyValidatingElement(%);
}
}
}
function coerceStringRef(mixedRef, owner, type) {
if (disableStringRefs) {
return mixedRef;
}
let stringRef;
if (typeof mixedRef === 'string') {
stringRef = mixedRef;
} else {
if (typeof mixedRef === 'number' || typeof mixedRef === 'boolean') {
if (__DEV__) {
mixedRef |> checkPropStringCoercion(%, 'ref');
}
stringRef = '' + mixedRef;
} else {
return mixedRef;
}
}
const callback = stringRefAsCallbackRef.bind(null, stringRef, type, owner);
// This is used to check whether two callback refs conceptually represent
// the same string ref, and can therefore be reused by the reconciler. Needed
// for backwards compatibility with old Meta code that relies on string refs
// not being reattached on every render.
callback.__stringRef = stringRef;
callback.__type = type;
callback.__owner = owner;
return callback;
}
function stringRefAsCallbackRef(stringRef, type, owner, value) {
if (disableStringRefs) {
return;
}
if (!owner) {
throw new Error(`Element ref was specified as a string (${stringRef}) but no owner was set. This could happen for one of` + ' the following reasons:\n' + '1. You may be adding a ref to a function component\n' + "2. You may be adding a ref to a component that was not created inside a component's render method\n" + '3. You have multiple copies of React loaded\n' + 'See https://react.dev/link/refs-must-have-owner for more information.');
}
if (owner.tag !== ClassComponent) {
throw new Error('Function components cannot have string refs. ' + 'We recommend using useRef() instead. ' + 'Learn more about using refs safely here: ' + 'https://react.dev/link/strict-mode-string-ref');
}
if (__DEV__) {
if (
// Will already warn with "Function components cannot be given refs"
!(typeof type === 'function' && !(type |> isReactClass(%)))) {
const componentName = owner |> getComponentNameFromFiber(%) || 'Component';
if (!didWarnAboutStringRefs[componentName]) {
console.error('Component "%s" contains the string ref "%s". Support for string refs ' + 'will be removed in a future major release. We recommend using ' + 'useRef() or createRef() instead. ' + 'Learn more about using refs safely here: ' + 'https://react.dev/link/strict-mode-string-ref', componentName, stringRef);
didWarnAboutStringRefs[componentName] = true;
}
}
}
const inst = owner.stateNode;
if (!inst) {
throw new Error(`Missing owner for string ref ${stringRef}. This error is likely caused by a ` + 'bug in React. Please file an issue.');
}
const refs = inst.refs;
if (value === null) {
delete refs[stringRef];
} else {
refs[stringRef] = value;
}
}
function isReactClass(type) {
return type.prototype && type.prototype.isReactComponent;
}