UNPKG

10.2 kBJavaScriptView Raw
1"use strict";
2
3Object.defineProperty(exports, "__esModule", {
4 value: true
5});
6exports.getValueWithBranchingLogicApplied = getValueWithBranchingLogicApplied;
7exports.wrapReactElementInBranchOrReturnValue = wrapReactElementInBranchOrReturnValue;
8
9var _realm = require("../realm.js");
10
11var _index = require("../values/index.js");
12
13var _invariant = _interopRequireDefault(require("../invariant.js"));
14
15var _index2 = require("../domains/index.js");
16
17var _utils = require("./utils.js");
18
19var _errors = require("./errors.js");
20
21var _generator = require("../utils/generator.js");
22
23function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
24
25/**
26 * Copyright (c) 2017-present, Facebook, Inc.
27 * All rights reserved.
28 *
29 * This source code is licensed under the BSD-style license found in the
30 * LICENSE file in the root directory of this source tree. An additional grant
31 * of patent rights can be found in the PATENTS file in the same directory.
32 */
33
34/* strict-local */
35// This function aims to determine if we need to add keys to the ReactElements
36// of the returned conditional abstract value branches. It does this by first
37// checking the parent branch nodes (these were use to render both respective branches)
38// for any cases where ReactElement types on host components mismatch.
39// Note: this implementation is not fully sound and is likely missing support
40// for all React reconcilation cases for handling of keys, see issue #1131
41function getValueWithBranchingLogicApplied(realm, parentX, parentY, value) {
42 let needsKeys = false; // we check the inlined value and see if the component types match
43
44 const searchAndFlagMatchingComponentTypes = (x, y, xTypeParent, yTypeParent) => {
45 // The returned value is the result of getting the "render" from a component.
46 // We need to search the value returned to see if the nodes need keys adding to them.
47 // 1. If we have <X? /> and <Y? />, then check if their
48 // types are the same, if they are the same and the parent types
49 // are not the same as then we need to add keys
50 if (x instanceof _index.ObjectValue && (0, _utils.isReactElement)(x) && y instanceof _index.ObjectValue && (0, _utils.isReactElement)(y)) {
51 let xType = (0, _utils.getProperty)(realm, x, "type");
52 let yType = (0, _utils.getProperty)(realm, y, "type");
53
54 if (xType.equals(yType) && !xTypeParent.equals(xType) && !yTypeParent.equals(yType)) {
55 needsKeys = true;
56 }
57 } else if (x instanceof _index.ArrayValue) {
58 // If we have x: []
59 // Go through the elements of array x
60 (0, _utils.forEachArrayValue)(realm, x, (xElem, index) => {
61 let yElem = y; // And if we also have y: [], with a given element from x
62 // search element of y at the same index from x.
63 // If y is not an array, then continue but use x: [] against y
64
65 if (y instanceof _index.ArrayValue) {
66 yElem = (0, _utils.getProperty)(realm, y, index + "");
67 }
68
69 searchAndFlagMatchingComponentTypes(xElem, yElem, xTypeParent, yTypeParent);
70 });
71 } else if (y instanceof _index.ArrayValue) {
72 // If we have y: []
73 // Go through the elements of array y
74 (0, _utils.forEachArrayValue)(realm, y, (yElem, index) => {
75 let xElem = x; // And if we also have x: [], with a given element from y
76 // search element of x at the same index from y.
77 // If x is not an array, then continue but use y: [] against x
78
79 if (x instanceof _index.ArrayValue) {
80 xElem = (0, _utils.getProperty)(realm, x, index + "");
81 }
82
83 searchAndFlagMatchingComponentTypes(xElem, yElem, xTypeParent, yTypeParent);
84 });
85 } else if (x instanceof _index.AbstractValue && x.kind === "conditional") {
86 // if x is a conditional value like "a ? b : c",
87 let [, consequentVal, alternateVal] = x.args;
88 searchAndFlagMatchingComponentTypes(consequentVal, y, xTypeParent, yTypeParent);
89 searchAndFlagMatchingComponentTypes(alternateVal, y, xTypeParent, yTypeParent);
90 } else if (y instanceof _index.AbstractValue && y.kind === "conditional") {
91 // if y is a conditional value like "a ? b : c",
92 let [, consequentVal, alternateVal] = y.args;
93 searchAndFlagMatchingComponentTypes(x, consequentVal, xTypeParent, yTypeParent);
94 searchAndFlagMatchingComponentTypes(x, alternateVal, xTypeParent, yTypeParent);
95 }
96 }; // we first check our "parent" value, that was used to get the inlined value
97
98
99 const searchAndFlagMismatchingNonHostTypes = (x, y, arrayDepth) => {
100 if (x instanceof _index.ObjectValue && (0, _utils.isReactElement)(x) && y instanceof _index.ObjectValue && (0, _utils.isReactElement)(y)) {
101 let xType = (0, _utils.getProperty)(realm, x, "type");
102 let yType = (0, _utils.getProperty)(realm, y, "type");
103
104 if (xType instanceof _index.StringValue && yType instanceof _index.StringValue) {
105 let xProps = (0, _utils.getProperty)(realm, x, "props");
106 let yProps = (0, _utils.getProperty)(realm, y, "props");
107
108 if (xProps instanceof _index.ObjectValue && yProps instanceof _index.ObjectValue) {
109 let xChildren = (0, _utils.getProperty)(realm, xProps, "children");
110 let yChildren = (0, _utils.getProperty)(realm, yProps, "children");
111
112 if (xChildren instanceof _index.Value && yChildren instanceof _index.Value) {
113 searchAndFlagMismatchingNonHostTypes(xChildren, yChildren, arrayDepth);
114 }
115 }
116 } else if (!xType.equals(yType)) {
117 let [, xVal, yVal] = value.args;
118 searchAndFlagMatchingComponentTypes(xVal, yVal, xType, yType);
119 }
120 } else if (_index.ArrayValue.isIntrinsicAndHasWidenedNumericProperty(x) || _index.ArrayValue.isIntrinsicAndHasWidenedNumericProperty(y)) {// If either case is an unknown array, we do not know
121 // the contents of the array, so we cannot add keys
122 } else if (x instanceof _index.ArrayValue && arrayDepth === 0) {
123 (0, _utils.forEachArrayValue)(realm, x, (xElem, index) => {
124 let yElem;
125
126 if (y instanceof _index.ArrayValue) {
127 // handle the case of [x].equals([y])
128 yElem = (0, _utils.getProperty)(realm, y, index + "");
129 } else if (index === 0) {
130 // handle the case of [x].equals(y)
131 yElem = y;
132 }
133
134 if (xElem instanceof _index.Value && yElem instanceof _index.Value) {
135 searchAndFlagMismatchingNonHostTypes(xElem, yElem, arrayDepth + 1);
136 }
137 });
138 } else if (y instanceof _index.ArrayValue && arrayDepth === 0) {
139 (0, _utils.forEachArrayValue)(realm, y, (yElem, index) => {
140 let xElem;
141
142 if (x instanceof _index.ArrayValue) {
143 // handle the case of [y].equals([x]
144 xElem = (0, _utils.getProperty)(realm, x, index + "");
145 } else if (index === 0) {
146 // handle the case of [y].equals(x)
147 xElem = x;
148 }
149
150 if (xElem instanceof _index.Value && yElem instanceof _index.Value) {
151 searchAndFlagMismatchingNonHostTypes(xElem, yElem, arrayDepth + 1);
152 }
153 });
154 }
155 };
156
157 searchAndFlagMismatchingNonHostTypes(parentX, parentY, 0);
158
159 if (needsKeys) {
160 return applyBranchedLogicValue(realm, value);
161 }
162
163 return value;
164} // When we apply branching logic, it means to add keys to all ReactElement nodes
165// we encounter, thus returning new ReactElements with the keys on them
166
167
168function applyBranchedLogicValue(realm, value) {
169 if (value instanceof _index.StringValue || value instanceof _index.NumberValue || value instanceof _index.BooleanValue || value instanceof _index.NullValue || value instanceof _index.UndefinedValue) {// terminal values
170 } else if (value instanceof _index.ObjectValue && (0, _utils.isReactElement)(value)) {
171 return (0, _utils.addKeyToReactElement)(realm, value);
172 } else if (value instanceof _index.ArrayValue) {
173 let newArray = (0, _utils.mapArrayValue)(realm, value, elementValue => applyBranchedLogicValue(realm, elementValue));
174 newArray.makeFinal();
175 return newArray;
176 } else if (value instanceof _index.AbstractValue && value.kind === "conditional") {
177 let [condValue, consequentVal, alternateVal] = value.args;
178 (0, _invariant.default)(condValue instanceof _index.AbstractValue);
179 return realm.evaluateWithAbstractConditional(condValue, () => {
180 return realm.evaluateForEffects(() => wrapReactElementInBranchOrReturnValue(realm, applyBranchedLogicValue(realm, consequentVal)), null, "applyBranchedLogicValue consequent");
181 }, () => {
182 return realm.evaluateForEffects(() => wrapReactElementInBranchOrReturnValue(realm, applyBranchedLogicValue(realm, alternateVal)), null, "applyBranchedLogicValue alternate");
183 });
184 } else if (value instanceof _index.AbstractValue && (value.kind === "||" || value.kind === "&&")) {
185 (0, _invariant.default)(false, "applyBranchedLogicValue encounterted a logical expression (|| or &&), this should never occur");
186 } else {
187 throw new _errors.ExpectedBailOut("Unsupported value encountered when applying branched logic to values");
188 }
189
190 return value;
191} // when a ReactElement is resolved in a conditional branch we
192// can improve runtime performance by ensuring that the ReactElement
193// is only created lazily in that specific branch and referenced
194// from then on. To do this we create a temporal abstract value
195// and set its kind to "branched ReactElement" so we properly track
196// the original ReactElement. If we don't have a ReactElement,
197// return the original value
198
199
200function wrapReactElementInBranchOrReturnValue(realm, value) {
201 if (value instanceof _index.ObjectValue && (0, _utils.isReactElement)(value)) {
202 let temporal = _index.AbstractValue.createTemporalFromBuildFunction(realm, _index.ObjectValue, [(0, _utils.cloneReactElement)(realm, value, false)], (0, _generator.createOperationDescriptor)("SINGLE_ARG"), {
203 isPure: true,
204 skipInvariant: true
205 });
206
207 (0, _invariant.default)(temporal instanceof _index.AbstractObjectValue);
208 temporal.values = new _index2.ValuesDomain(value);
209 value.temporalAlias = temporal;
210 }
211
212 return value;
213}
214//# sourceMappingURL=branching.js.map
\No newline at end of file