UNPKG

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
"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