prepack
Version:
Execute a JS bundle, serialize global state and side effects to a snapshot that can be quickly restored.
422 lines (333 loc) • 20 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.cloneReactElement = cloneReactElement;
exports.createReactElement = createReactElement;
exports.wrapReactElementWithKeyedFragment = wrapReactElementWithKeyedFragment;
exports.traverseReactElement = traverseReactElement;
var _index = require("../domains/index.js");
var _index2 = require("../values/index.js");
var _singletons = require("../singletons.js");
var _invariant = _interopRequireDefault(require("../invariant.js"));
var _index3 = require("../methods/index.js");
var _utils = require("./utils.js");
var _BinaryExpression = require("../evaluators/BinaryExpression.js");
var _errors = require("../errors.js");
var _generator = require("../utils/generator.js");
var _descriptors = require("../descriptors.js");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
function createPropsObject(realm, type, config, children) {
// If we're in "rendering" a React component tree, we should have an active reconciler
let activeReconciler = realm.react.activeReconciler;
let firstRenderOnly = activeReconciler !== undefined ? activeReconciler.componentTreeConfig.firstRenderOnly : false;
let defaultProps = type instanceof _index2.ObjectValue || type instanceof _index2.AbstractObjectValue ? (0, _index3.Get)(realm, type, "defaultProps") : realm.intrinsics.undefined;
let props = _singletons.Create.ObjectCreate(realm, realm.intrinsics.ObjectPrototype);
props.makeFinal();
realm.react.reactProps.add(props);
let key = realm.intrinsics.null;
let ref = realm.intrinsics.null;
if (!(0, _utils.hasNoPartialKeyOrRef)(realm, config)) {
// if either are abstract, this will impact the reconcilation process
// and ultimately prevent us from folding ReactElements properly
let diagnostic = new _errors.CompilerDiagnostic(`unable to evaluate "key" and "ref" on a ReactElement due to an abstract config passed to createElement`, realm.currentLocation, "PP0025", "FatalError");
realm.handleError(diagnostic);
if (realm.handleError(diagnostic) === "Fail") throw new _errors.FatalError();
}
let possibleKey = (0, _index3.Get)(realm, config, "key");
if (possibleKey !== realm.intrinsics.null && possibleKey !== realm.intrinsics.undefined) {
// if the config has been marked as having no partial key or ref and the possible key
// is abstract, yet the config doesn't have a key property, then the key can remain null
let keyNotNeeded = (0, _utils.hasNoPartialKeyOrRef)(realm, config) && possibleKey instanceof _index2.AbstractValue && config instanceof _index2.ObjectValue && !config.properties.has("key");
if (!keyNotNeeded) {
key = (0, _BinaryExpression.computeBinary)(realm, "+", realm.intrinsics.emptyString, possibleKey);
}
}
let possibleRef = (0, _index3.Get)(realm, config, "ref");
if (possibleRef !== realm.intrinsics.null && possibleRef !== realm.intrinsics.undefined && !firstRenderOnly) {
// if the config has been marked as having no partial key or ref and the possible ref
// is abstract, yet the config doesn't have a ref property, then the ref can remain null
let refNotNeeded = (0, _utils.hasNoPartialKeyOrRef)(realm, config) && possibleRef instanceof _index2.AbstractValue && config instanceof _index2.ObjectValue && !config.properties.has("ref");
if (!refNotNeeded) {
ref = possibleRef;
}
}
const setProp = (name, value) => {
if (name !== "__self" && name !== "__source" && name !== "key" && name !== "ref") {
(0, _invariant.default)(props instanceof _index2.ObjectValue);
(0, _utils.hardModifyReactObjectPropertyBinding)(realm, props, name, value);
}
};
const applyProperties = () => {
if (config instanceof _index2.ObjectValue) {
for (let [propKey, binding] of config.properties) {
if (binding && binding.descriptor) {
(0, _invariant.default)(binding.descriptor instanceof _descriptors.PropertyDescriptor);
if (binding.descriptor.enumerable) {
setProp(propKey, (0, _index3.Get)(realm, config, propKey));
}
}
}
}
};
if (config instanceof _index2.AbstractObjectValue && config.isPartialObject() || config instanceof _index2.ObjectValue && config.isPartialObject() && config.isSimpleObject()) {
// create a new props object that will be the target of the Object.assign
props = _singletons.Create.ObjectCreate(realm, realm.intrinsics.ObjectPrototype);
realm.react.reactProps.add(props);
(0, _utils.applyObjectAssignConfigsForReactElement)(realm, props, [config]);
props.makeFinal();
if (children !== undefined) {
(0, _utils.hardModifyReactObjectPropertyBinding)(realm, props, "children", children);
} // handle default props on a partial/abstract config
if (defaultProps !== realm.intrinsics.undefined) {
let defaultPropsEvaluated = 0; // first see if we can apply all the defaultProps without needing the helper
if (defaultProps instanceof _index2.ObjectValue && !defaultProps.isPartialObject()) {
for (let [propName, binding] of defaultProps.properties) {
if (binding.descriptor !== undefined) {
(0, _invariant.default)(binding.descriptor instanceof _descriptors.PropertyDescriptor);
if (binding.descriptor.value !== realm.intrinsics.undefined) {
// see if we have this on our props object
let propBinding = props.properties.get(propName); // if the binding exists and value is abstract, it might be undefined
// so in that case we need the helper, otherwise we can continue
if (propBinding !== undefined && !(propBinding.descriptor instanceof _descriptors.PropertyDescriptor && propBinding.descriptor.value instanceof _index2.AbstractValue)) {
defaultPropsEvaluated++; // if the value we have is undefined, we can apply the defaultProp
if (propBinding.descriptor) {
(0, _invariant.default)(propBinding.descriptor instanceof _descriptors.PropertyDescriptor);
if (propBinding.descriptor.value === realm.intrinsics.undefined) (0, _utils.hardModifyReactObjectPropertyBinding)(realm, props, propName, (0, _index3.Get)(realm, defaultProps, propName));
}
}
}
}
}
} // if defaultPropsEvaluated === the amount of properties defaultProps has, then we've successfully
// ensured all the defaultProps have already been dealt with, so we don't need the helper
if (!(defaultProps instanceof _index2.ObjectValue) || defaultProps.isPartialObject() || defaultPropsEvaluated !== defaultProps.properties.size) {
props.makePartial();
props.makeSimple(); // if the props has any properties that are "undefined", we need to make them abstract
// as the helper function applies defaultProps on values that are undefined or do not
// exist
for (let [propName, binding] of props.properties) {
if (binding.descriptor !== undefined) {
(0, _invariant.default)(binding.descriptor instanceof _descriptors.PropertyDescriptor);
if (binding.descriptor.value === realm.intrinsics.undefined) {
(0, _invariant.default)(defaultProps instanceof _index2.AbstractObjectValue || defaultProps instanceof _index2.ObjectValue);
(0, _utils.hardModifyReactObjectPropertyBinding)(realm, props, propName, (0, _index3.Get)(realm, defaultProps, propName));
}
}
} // if we have children and they are abstract, they might be undefined at runtime
if (children !== undefined && children instanceof _index2.AbstractValue) {
// children === undefined ? defaultProps.children : children;
let condition = _index2.AbstractValue.createFromBinaryOp(realm, "===", children, realm.intrinsics.undefined);
(0, _invariant.default)(defaultProps instanceof _index2.AbstractObjectValue || defaultProps instanceof _index2.ObjectValue);
let conditionalChildren = _index2.AbstractValue.createFromConditionalOp(realm, condition, (0, _index3.Get)(realm, defaultProps, "children"), children);
(0, _utils.hardModifyReactObjectPropertyBinding)(realm, props, "children", conditionalChildren);
}
let defaultPropsHelper = realm.react.defaultPropsHelper;
(0, _invariant.default)(defaultPropsHelper !== undefined);
let snapshot = props.getSnapshot();
props.temporalAlias = snapshot;
let temporalArgs = [defaultPropsHelper, snapshot, defaultProps];
let temporalTo = _index2.AbstractValue.createTemporalFromBuildFunction(realm, _index2.ObjectValue, temporalArgs, (0, _generator.createOperationDescriptor)("REACT_DEFAULT_PROPS_HELPER"), {
skipInvariant: true,
mutatesOnly: [snapshot]
});
(0, _invariant.default)(temporalTo instanceof _index2.AbstractObjectValue);
if (props instanceof _index2.AbstractObjectValue) {
temporalTo.values = props.values;
} else {
(0, _invariant.default)(props instanceof _index2.ObjectValue);
temporalTo.values = new _index.ValuesDomain(props);
}
props.temporalAlias = temporalTo;
}
}
} else {
applyProperties();
if (children !== undefined) {
setProp("children", children);
}
if (defaultProps instanceof _index2.AbstractObjectValue || defaultProps.isPartialObject()) {
(0, _invariant.default)(false, "TODO: we need to eventually support this");
} else if (defaultProps instanceof _index2.ObjectValue) {
for (let [propKey, binding] of defaultProps.properties) {
if (binding && binding.descriptor) {
(0, _invariant.default)(binding.descriptor instanceof _descriptors.PropertyDescriptor);
if (binding.descriptor.enumerable && (0, _index3.Get)(realm, props, propKey) === realm.intrinsics.undefined) {
setProp(propKey, (0, _index3.Get)(realm, defaultProps, propKey));
}
}
}
}
}
(0, _invariant.default)(props instanceof _index2.ObjectValue); // We know the props has no keys because if it did it would have thrown above
// so we can remove them the props we create.
(0, _utils.flagPropsWithNoPartialKeyOrRef)(realm, props);
return {
key,
props,
ref
};
}
function splitReactElementsByConditionalType(realm, condValue, consequentVal, alternateVal, config, children) {
return realm.evaluateWithAbstractConditional(condValue, () => {
return realm.evaluateForEffects(() => createReactElement(realm, consequentVal, config, children), null, "splitReactElementsByConditionalType consequent");
}, () => {
return realm.evaluateForEffects(() => createReactElement(realm, alternateVal, config, children), null, "splitReactElementsByConditionalType alternate");
});
}
function splitReactElementsByConditionalConfig(realm, condValue, consequentVal, alternateVal, type, children) {
return realm.evaluateWithAbstractConditional(condValue, () => {
return realm.evaluateForEffects(() => createReactElement(realm, type, consequentVal, children), null, "splitReactElementsByConditionalConfig consequent");
}, () => {
return realm.evaluateForEffects(() => createReactElement(realm, type, alternateVal, children), null, "splitReactElementsByConditionalConfig alternate");
});
}
function cloneReactElement(realm, reactElement, config, children) {
let props = _singletons.Create.ObjectCreate(realm, realm.intrinsics.ObjectPrototype);
realm.react.reactProps.add(props);
const setProp = (name, value) => {
if (name !== "__self" && name !== "__source" && name !== "key" && name !== "ref") {
(0, _invariant.default)(props instanceof _index2.ObjectValue);
(0, _utils.hardModifyReactObjectPropertyBinding)(realm, props, name, value);
}
};
let elementProps = (0, _utils.getProperty)(realm, reactElement, "props");
(0, _utils.applyObjectAssignConfigsForReactElement)(realm, props, [elementProps, config]);
props.makeFinal();
let key = (0, _utils.getProperty)(realm, reactElement, "key");
let ref = (0, _utils.getProperty)(realm, reactElement, "ref");
let type = (0, _utils.getProperty)(realm, reactElement, "type");
if (!(config instanceof _index2.NullValue)) {
let possibleKey = (0, _index3.Get)(realm, config, "key");
if (possibleKey !== realm.intrinsics.null && possibleKey !== realm.intrinsics.undefined) {
// if the config has been marked as having no partial key or ref and the possible key
// is abstract, yet the config doesn't have a key property, then the key can remain null
let keyNotNeeded = (0, _utils.hasNoPartialKeyOrRef)(realm, config) && possibleKey instanceof _index2.AbstractValue && config instanceof _index2.ObjectValue && !config.properties.has("key");
if (!keyNotNeeded) {
key = (0, _BinaryExpression.computeBinary)(realm, "+", realm.intrinsics.emptyString, possibleKey);
}
}
let possibleRef = (0, _index3.Get)(realm, config, "ref");
if (possibleRef !== realm.intrinsics.null && possibleRef !== realm.intrinsics.undefined) {
// if the config has been marked as having no partial key or ref and the possible ref
// is abstract, yet the config doesn't have a ref property, then the ref can remain null
let refNotNeeded = (0, _utils.hasNoPartialKeyOrRef)(realm, config) && possibleRef instanceof _index2.AbstractValue && config instanceof _index2.ObjectValue && !config.properties.has("ref");
if (!refNotNeeded) {
ref = possibleRef;
}
}
let defaultProps = type instanceof _index2.ObjectValue || type instanceof _index2.AbstractObjectValue ? (0, _index3.Get)(realm, type, "defaultProps") : realm.intrinsics.undefined;
if (defaultProps instanceof _index2.ObjectValue) {
for (let [propKey, binding] of defaultProps.properties) {
if (binding && binding.descriptor) {
(0, _invariant.default)(binding.descriptor instanceof _descriptors.PropertyDescriptor);
if (binding.descriptor.enumerable && (0, _index3.Get)(realm, props, propKey) === realm.intrinsics.undefined) {
setProp(propKey, (0, _index3.Get)(realm, defaultProps, propKey));
}
}
}
} else if (defaultProps instanceof _index2.AbstractObjectValue) {
(0, _invariant.default)(false, "TODO: we need to eventually support this");
}
}
if (children !== undefined) {
(0, _utils.hardModifyReactObjectPropertyBinding)(realm, props, "children", children);
} else {
(0, _invariant.default)(elementProps instanceof _index2.ObjectValue);
let elementChildren = (0, _utils.getProperty)(realm, elementProps, "children");
(0, _utils.hardModifyReactObjectPropertyBinding)(realm, props, "children", elementChildren);
}
return (0, _utils.createInternalReactElement)(realm, type, key, ref, props);
}
function createReactElement(realm, type, config, children) {
if (type instanceof _index2.AbstractValue && type.kind === "conditional") {
let [condValue, consequentVal, alternateVal] = type.args;
(0, _invariant.default)(condValue instanceof _index2.AbstractValue);
return splitReactElementsByConditionalType(realm, condValue, consequentVal, alternateVal, config, children);
} else if (config instanceof _index2.AbstractObjectValue && config.kind === "conditional") {
let [condValue, consequentVal, alternateVal] = config.args;
(0, _invariant.default)(condValue instanceof _index2.AbstractValue);
(0, _invariant.default)(consequentVal instanceof _index2.ObjectValue || consequentVal instanceof _index2.AbstractObjectValue);
(0, _invariant.default)(alternateVal instanceof _index2.ObjectValue || alternateVal instanceof _index2.AbstractObjectValue);
return splitReactElementsByConditionalConfig(realm, condValue, consequentVal, alternateVal, type, children);
}
let {
key,
props,
ref
} = createPropsObject(realm, type, config, children);
return (0, _utils.createInternalReactElement)(realm, type, key, ref, props);
} // Wraps a React element in a `<React.Fragment key={keyValue}>` so that we can
// add a key without mutating or cloning the element.
function wrapReactElementWithKeyedFragment(realm, keyValue, reactElement) {
const react = realm.fbLibraries.react;
(0, _invariant.default)(react instanceof _index2.ObjectValue);
const reactFragment = (0, _utils.getProperty)(realm, react, "Fragment");
const fragmentConfigValue = _singletons.Create.ObjectCreate(realm, realm.intrinsics.ObjectPrototype);
_singletons.Create.CreateDataPropertyOrThrow(realm, fragmentConfigValue, "key", keyValue);
let fragmentChildrenValue = _singletons.Create.ArrayCreate(realm, 1);
_singletons.Create.CreateDataPropertyOrThrow(realm, fragmentChildrenValue, "0", reactElement);
fragmentChildrenValue = (0, _utils.flattenChildren)(realm, fragmentChildrenValue, true);
return createReactElement(realm, reactFragment, fragmentConfigValue, fragmentChildrenValue);
}
function traverseReactElement(realm, reactElement, traversalVisitor) {
let typeValue = (0, _utils.getProperty)(realm, reactElement, "type");
traversalVisitor.visitType(typeValue);
let keyValue = (0, _utils.getProperty)(realm, reactElement, "key");
if (keyValue !== realm.intrinsics.null && keyValue !== realm.intrinsics.undefined) {
traversalVisitor.visitKey(keyValue);
}
let refValue = (0, _utils.getProperty)(realm, reactElement, "ref");
if (refValue !== realm.intrinsics.null && refValue !== realm.intrinsics.undefined) {
traversalVisitor.visitRef(refValue);
}
const loopArrayElements = (childrenValue, length) => {
for (let i = 0; i < length; i++) {
let child = (0, _utils.getProperty)(realm, childrenValue, "" + i);
traversalVisitor.visitChildNode(child);
}
};
const handleChildren = () => {
// handle children
(0, _invariant.default)(propsValue instanceof _index2.ObjectValue);
if (propsValue.properties.has("children")) {
let childrenValue = (0, _utils.getProperty)(realm, propsValue, "children");
if (childrenValue !== realm.intrinsics.undefined && childrenValue !== realm.intrinsics.null) {
if (childrenValue instanceof _index2.ArrayValue && !childrenValue.intrinsicName) {
let childrenLength = (0, _utils.getProperty)(realm, childrenValue, "length");
if (childrenLength instanceof _index2.NumberValue) {
loopArrayElements(childrenValue, childrenLength.value);
} else if (childrenLength instanceof _index2.AbstractValue && childrenLength.kind === "conditional") {
loopArrayElements(childrenValue, (0, _utils.getMaxLength)(childrenLength, 0));
} else {
(0, _invariant.default)(false, "TODO: support other types of array length value");
}
} else {
traversalVisitor.visitChildNode(childrenValue);
}
}
}
};
let propsValue = (0, _utils.getProperty)(realm, reactElement, "props");
if (propsValue instanceof _index2.AbstractValue) {
// visit object, as it's going to be spread
traversalVisitor.visitAbstractOrPartialProps(propsValue);
} else if (propsValue instanceof _index2.ObjectValue) {
if (propsValue.isPartialObject()) {
traversalVisitor.visitAbstractOrPartialProps(propsValue);
handleChildren();
} else {
traversalVisitor.visitConcreteProps(propsValue);
handleChildren();
}
}
}
//# sourceMappingURL=elements.js.map