UNPKG

prepack

Version:

Execute a JS bundle, serialize global state and side effects to a snapshot that can be quickly restored.

214 lines (178 loc) 10.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.getValueWithBranchingLogicApplied = getValueWithBranchingLogicApplied; exports.wrapReactElementInBranchOrReturnValue = wrapReactElementInBranchOrReturnValue; var _realm = require("../realm.js"); var _index = require("../values/index.js"); var _invariant = _interopRequireDefault(require("../invariant.js")); var _index2 = require("../domains/index.js"); var _utils = require("./utils.js"); var _errors = require("./errors.js"); var _generator = require("../utils/generator.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. */ /* strict-local */ // This function aims to determine if we need to add keys to the ReactElements // of the returned conditional abstract value branches. It does this by first // checking the parent branch nodes (these were use to render both respective branches) // for any cases where ReactElement types on host components mismatch. // Note: this implementation is not fully sound and is likely missing support // for all React reconcilation cases for handling of keys, see issue #1131 function getValueWithBranchingLogicApplied(realm, parentX, parentY, value) { let needsKeys = false; // we check the inlined value and see if the component types match const searchAndFlagMatchingComponentTypes = (x, y, xTypeParent, yTypeParent) => { // The returned value is the result of getting the "render" from a component. // We need to search the value returned to see if the nodes need keys adding to them. // 1. If we have <X? /> and <Y? />, then check if their // types are the same, if they are the same and the parent types // are not the same as then we need to add keys if (x instanceof _index.ObjectValue && (0, _utils.isReactElement)(x) && y instanceof _index.ObjectValue && (0, _utils.isReactElement)(y)) { let xType = (0, _utils.getProperty)(realm, x, "type"); let yType = (0, _utils.getProperty)(realm, y, "type"); if (xType.equals(yType) && !xTypeParent.equals(xType) && !yTypeParent.equals(yType)) { needsKeys = true; } } else if (x instanceof _index.ArrayValue) { // If we have x: [] // Go through the elements of array x (0, _utils.forEachArrayValue)(realm, x, (xElem, index) => { let yElem = y; // And if we also have y: [], with a given element from x // search element of y at the same index from x. // If y is not an array, then continue but use x: [] against y if (y instanceof _index.ArrayValue) { yElem = (0, _utils.getProperty)(realm, y, index + ""); } searchAndFlagMatchingComponentTypes(xElem, yElem, xTypeParent, yTypeParent); }); } else if (y instanceof _index.ArrayValue) { // If we have y: [] // Go through the elements of array y (0, _utils.forEachArrayValue)(realm, y, (yElem, index) => { let xElem = x; // And if we also have x: [], with a given element from y // search element of x at the same index from y. // If x is not an array, then continue but use y: [] against x if (x instanceof _index.ArrayValue) { xElem = (0, _utils.getProperty)(realm, x, index + ""); } searchAndFlagMatchingComponentTypes(xElem, yElem, xTypeParent, yTypeParent); }); } else if (x instanceof _index.AbstractValue && x.kind === "conditional") { // if x is a conditional value like "a ? b : c", let [, consequentVal, alternateVal] = x.args; searchAndFlagMatchingComponentTypes(consequentVal, y, xTypeParent, yTypeParent); searchAndFlagMatchingComponentTypes(alternateVal, y, xTypeParent, yTypeParent); } else if (y instanceof _index.AbstractValue && y.kind === "conditional") { // if y is a conditional value like "a ? b : c", let [, consequentVal, alternateVal] = y.args; searchAndFlagMatchingComponentTypes(x, consequentVal, xTypeParent, yTypeParent); searchAndFlagMatchingComponentTypes(x, alternateVal, xTypeParent, yTypeParent); } }; // we first check our "parent" value, that was used to get the inlined value const searchAndFlagMismatchingNonHostTypes = (x, y, arrayDepth) => { if (x instanceof _index.ObjectValue && (0, _utils.isReactElement)(x) && y instanceof _index.ObjectValue && (0, _utils.isReactElement)(y)) { let xType = (0, _utils.getProperty)(realm, x, "type"); let yType = (0, _utils.getProperty)(realm, y, "type"); if (xType instanceof _index.StringValue && yType instanceof _index.StringValue) { let xProps = (0, _utils.getProperty)(realm, x, "props"); let yProps = (0, _utils.getProperty)(realm, y, "props"); if (xProps instanceof _index.ObjectValue && yProps instanceof _index.ObjectValue) { let xChildren = (0, _utils.getProperty)(realm, xProps, "children"); let yChildren = (0, _utils.getProperty)(realm, yProps, "children"); if (xChildren instanceof _index.Value && yChildren instanceof _index.Value) { searchAndFlagMismatchingNonHostTypes(xChildren, yChildren, arrayDepth); } } } else if (!xType.equals(yType)) { let [, xVal, yVal] = value.args; searchAndFlagMatchingComponentTypes(xVal, yVal, xType, yType); } } else if (_index.ArrayValue.isIntrinsicAndHasWidenedNumericProperty(x) || _index.ArrayValue.isIntrinsicAndHasWidenedNumericProperty(y)) {// If either case is an array with wideneded properties, we do not know // the contents of the array, so we cannot add keys } else if (x instanceof _index.ArrayValue && arrayDepth === 0) { (0, _utils.forEachArrayValue)(realm, x, (xElem, index) => { let yElem; if (y instanceof _index.ArrayValue) { // handle the case of [x].equals([y]) yElem = (0, _utils.getProperty)(realm, y, index + ""); } else if (index === 0) { // handle the case of [x].equals(y) yElem = y; } if (xElem instanceof _index.Value && yElem instanceof _index.Value) { searchAndFlagMismatchingNonHostTypes(xElem, yElem, arrayDepth + 1); } }); } else if (y instanceof _index.ArrayValue && arrayDepth === 0) { (0, _utils.forEachArrayValue)(realm, y, (yElem, index) => { let xElem; if (x instanceof _index.ArrayValue) { // handle the case of [y].equals([x] xElem = (0, _utils.getProperty)(realm, x, index + ""); } else if (index === 0) { // handle the case of [y].equals(x) xElem = x; } if (xElem instanceof _index.Value && yElem instanceof _index.Value) { searchAndFlagMismatchingNonHostTypes(xElem, yElem, arrayDepth + 1); } }); } }; searchAndFlagMismatchingNonHostTypes(parentX, parentY, 0); if (needsKeys) { return applyBranchedLogicValue(realm, value); } return value; } // When we apply branching logic, it means to add keys to all ReactElement nodes // we encounter, thus returning new ReactElements with the keys on them function applyBranchedLogicValue(realm, value) { if (value instanceof _index.StringValue || value instanceof _index.NumberValue || value instanceof _index.BooleanValue || value instanceof _index.NullValue || value instanceof _index.UndefinedValue) {// terminal values } else if (value instanceof _index.ObjectValue && (0, _utils.isReactElement)(value)) { return (0, _utils.addKeyToReactElement)(realm, value); } else if (value instanceof _index.ArrayValue) { let newArray = (0, _utils.mapArrayValue)(realm, value, elementValue => applyBranchedLogicValue(realm, elementValue)); newArray.makeFinal(); return newArray; } else if (value instanceof _index.AbstractValue && value.kind === "conditional") { let [condValue, consequentVal, alternateVal] = value.args; (0, _invariant.default)(condValue instanceof _index.AbstractValue); return realm.evaluateWithAbstractConditional(condValue, () => { return realm.evaluateForEffects(() => wrapReactElementInBranchOrReturnValue(realm, applyBranchedLogicValue(realm, consequentVal)), null, "applyBranchedLogicValue consequent"); }, () => { return realm.evaluateForEffects(() => wrapReactElementInBranchOrReturnValue(realm, applyBranchedLogicValue(realm, alternateVal)), null, "applyBranchedLogicValue alternate"); }); } else if (value instanceof _index.AbstractValue && (value.kind === "||" || value.kind === "&&")) { (0, _invariant.default)(false, "applyBranchedLogicValue encounterted a logical expression (|| or &&), this should never occur"); } else { throw new _errors.ExpectedBailOut("Unsupported value encountered when applying branched logic to values"); } return value; } // when a ReactElement is resolved in a conditional branch we // can improve runtime performance by ensuring that the ReactElement // is only created lazily in that specific branch and referenced // from then on. To do this we create a temporal abstract value // and set its kind to "branched ReactElement" so we properly track // the original ReactElement. If we don't have a ReactElement, // return the original value function wrapReactElementInBranchOrReturnValue(realm, value) { if (value instanceof _index.ObjectValue && (0, _utils.isReactElement)(value)) { let temporal = _index.AbstractValue.createTemporalFromBuildFunction(realm, _index.ObjectValue, [(0, _utils.cloneReactElement)(realm, value, false)], (0, _generator.createOperationDescriptor)("SINGLE_ARG"), { isPure: true, skipInvariant: true }); (0, _invariant.default)(temporal instanceof _index.AbstractObjectValue); temporal.values = new _index2.ValuesDomain(value); value.temporalAlias = temporal; } return value; } //# sourceMappingURL=branching.js.map