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
JavaScript
;
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