prepack
Version:
Execute a JS bundle, serialize global state and side effects to a snapshot that can be quickly restored.
783 lines (696 loc) • 31.7 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.isReactElement = isReactElement;
exports.getReactSymbol = getReactSymbol;
exports.isTagName = isTagName;
exports.isReactComponent = isReactComponent;
exports.valueIsClassComponent = valueIsClassComponent;
exports.valueIsKnownReactAbstraction = valueIsKnownReactAbstraction;
exports.valueIsReactLibraryObject = valueIsReactLibraryObject;
exports.valueIsLegacyCreateClassComponent = valueIsLegacyCreateClassComponent;
exports.valueIsFactoryClassComponent = valueIsFactoryClassComponent;
exports.addKeyToReactElement = addKeyToReactElement;
exports.getUniqueReactElementKey = getUniqueReactElementKey;
exports.forEachArrayValue = forEachArrayValue;
exports.convertSimpleClassComponentToFunctionalComponent = convertSimpleClassComponentToFunctionalComponent;
exports.convertFunctionalComponentToComplexClassComponent = convertFunctionalComponentToComplexClassComponent;
exports.normalizeFunctionalComponentParamaters = normalizeFunctionalComponentParamaters;
exports.createReactHintObject = createReactHintObject;
exports.getComponentTypeFromRootValue = getComponentTypeFromRootValue;
exports.deleteRefAndKeyFromProps = deleteRefAndKeyFromProps;
exports.objectHasNoPartialKeyAndRef = objectHasNoPartialKeyAndRef;
exports.flattenChildren = flattenChildren;
exports.evaluateComponentTreeBranch = evaluateComponentTreeBranch;
exports.setProperty = setProperty;
exports.getProperty = getProperty;
exports.isRenderPropFunctionSelfContained = isRenderPropFunctionSelfContained;
exports.createReactEvaluatedNode = createReactEvaluatedNode;
exports.getComponentName = getComponentName;
exports.convertConfigObjectToReactComponentTreeConfig = convertConfigObjectToReactComponentTreeConfig;
exports.getValueFromRenderCall = getValueFromRenderCall;
exports.sanitizeReactElementForFirstRenderOnly = sanitizeReactElementForFirstRenderOnly;
var _realm = require("../realm.js");
var _environment = require("../environment.js");
var _completions = require("../completions.js");
var _index = require("../values/index.js");
var _generator = require("../utils/generator.js");
var _index2 = require("../methods/index.js");
var _BinaryExpression = require("../evaluators/BinaryExpression.js");
var _invariant = require("../invariant.js");
var _invariant2 = _interopRequireDefault(_invariant);
var _singletons = require("../singletons.js");
var _babelTraverse = require("babel-traverse");
var _babelTraverse2 = _interopRequireDefault(_babelTraverse);
var _babelTypes = require("babel-types");
var t = _interopRequireWildcard(_babelTypes);
var _errors = require("../errors.js");
var _errors2 = require("./errors.js");
var _AbstractValue = require("../values/AbstractValue");
var _AbstractValue2 = _interopRequireDefault(_AbstractValue);
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function isReactElement(val) {
if (!(val instanceof _index.ObjectValue)) {
return false;
}
let realm = val.$Realm;
if (!realm.react.enabled) {
return false;
}
if (realm.react.reactElements.has(val)) {
return true;
}
if (val.properties.has("$$typeof")) {
let $$typeof = (0, _index2.Get)(realm, val, "$$typeof");
let globalObject = realm.$GlobalObject;
let globalSymbolValue = (0, _index2.Get)(realm, globalObject, "Symbol");
if (globalSymbolValue === realm.intrinsics.undefined) {
if ($$typeof instanceof _index.NumberValue) {
return $$typeof.value === 0xeac7;
}
} else if ($$typeof instanceof _index.SymbolValue) {
let symbolFromRegistry = realm.globalSymbolRegistry.find(e => e.$Symbol === $$typeof);
let _isReactElement = symbolFromRegistry !== undefined && symbolFromRegistry.$Key === "react.element";
if (_isReactElement) {
// add to Set to speed up future lookups
realm.react.reactElements.add(val);
return true;
}
}
}
return false;
} /**
* 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 getReactSymbol(symbolKey, realm) {
let reactSymbol = realm.react.symbols.get(symbolKey);
if (reactSymbol !== undefined) {
return reactSymbol;
}
let SymbolFor = realm.intrinsics.Symbol.properties.get("for");
if (SymbolFor !== undefined) {
let SymbolForDescriptor = SymbolFor.descriptor;
if (SymbolForDescriptor !== undefined) {
let SymbolForValue = SymbolForDescriptor.value;
if (SymbolForValue !== undefined && typeof SymbolForValue.$Call === "function") {
reactSymbol = SymbolForValue.$Call(realm.intrinsics.Symbol, [new _index.StringValue(realm, symbolKey)]);
realm.react.symbols.set(symbolKey, reactSymbol);
}
}
}
(0, _invariant2.default)(reactSymbol instanceof _index.SymbolValue, `Symbol("${symbolKey}") could not be found in realm`);
return reactSymbol;
}
function isTagName(ast) {
return ast.type === "JSXIdentifier" && /^[a-z]|\-/.test(ast.name);
}
function isReactComponent(name) {
return name.length > 0 && name[0] === name[0].toUpperCase();
}
function valueIsClassComponent(realm, value) {
if (!(value instanceof _index.FunctionValue)) {
return false;
}
let prototype = (0, _index2.Get)(realm, value, "prototype");
if (prototype instanceof _index.ObjectValue) {
return _singletons.To.ToBooleanPartial(realm, (0, _index2.Get)(realm, prototype, "isReactComponent"));
}
return false;
}
function valueIsKnownReactAbstraction(realm, value) {
return value instanceof _index.AbstractObjectValue && realm.react.abstractHints.has(value);
}
// logger isn't typed otherwise it will increase flow cycle length :()
function valueIsReactLibraryObject(realm, value, logger) {
if (realm.fbLibraries.react === value) {
return true;
}
// we check that the object is the React or React-like library by checking for
// core properties that should exist on it
let reactVersion = logger.tryQuery(() => (0, _index2.Get)(realm, value, "version"), undefined);
if (!(reactVersion instanceof _index.StringValue)) {
return false;
}
let reactCreateElement = logger.tryQuery(() => (0, _index2.Get)(realm, value, "createElement"), undefined);
if (!(reactCreateElement instanceof _index.FunctionValue)) {
return false;
}
let reactCloneElement = logger.tryQuery(() => (0, _index2.Get)(realm, value, "cloneElement"), undefined);
if (!(reactCloneElement instanceof _index.FunctionValue)) {
return false;
}
let reactIsValidElement = logger.tryQuery(() => (0, _index2.Get)(realm, value, "isValidElement"), undefined);
if (!(reactIsValidElement instanceof _index.FunctionValue)) {
return false;
}
let reactComponent = logger.tryQuery(() => (0, _index2.Get)(realm, value, "Component"), undefined);
if (!(reactComponent instanceof _index.FunctionValue)) {
return false;
}
let reactChildren = logger.tryQuery(() => (0, _index2.Get)(realm, value, "Children"), undefined);
if (!(reactChildren instanceof _index.ObjectValue)) {
return false;
}
return false;
}
function valueIsLegacyCreateClassComponent(realm, value) {
if (!(value instanceof _index.FunctionValue)) {
return false;
}
let prototype = (0, _index2.Get)(realm, value, "prototype");
if (prototype instanceof _index.ObjectValue) {
return prototype.properties.has("__reactAutoBindPairs");
}
return false;
}
function valueIsFactoryClassComponent(realm, value) {
if (value instanceof _index.ObjectValue) {
return _singletons.To.ToBooleanPartial(realm, (0, _index2.Get)(realm, value, "render"));
}
return false;
}
function addKeyToReactElement(realm, reactSerializerState, reactElement) {
// we need to apply a key when we're branched
let currentKeyValue = (0, _index2.Get)(realm, reactElement, "key") || realm.intrinsics.null;
let uniqueKey = getUniqueReactElementKey("", reactSerializerState.usedReactElementKeys);
let newKeyValue = new _index.StringValue(realm, uniqueKey);
if (currentKeyValue !== realm.intrinsics.null) {
newKeyValue = (0, _BinaryExpression.computeBinary)(realm, "+", currentKeyValue, newKeyValue);
}
// TODO: This might not be safe in DEV because these objects are frozen (Object.freeze).
// We should probably go behind the scenes in this case to by-pass that.
reactElement.$Set("key", newKeyValue, reactElement);
}
// we create a unique key for each JSXElement to prevent collisions
// otherwise React will detect a missing/conflicting key at runtime and
// this can break the reconcilation of JSXElements in arrays
function getUniqueReactElementKey(index, usedReactElementKeys) {
let key;
do {
key = Math.random().toString(36).replace(/[^a-z]+/g, "").substring(0, 2);
} while (usedReactElementKeys.has(key));
usedReactElementKeys.add(key);
if (index !== undefined) {
return `${key}${index}`;
}
return key;
}
// a helper function to loop over ArrayValues
function forEachArrayValue(realm, array, mapFunc) {
let lengthValue = (0, _index2.Get)(realm, array, "length");
(0, _invariant2.default)(lengthValue instanceof _index.NumberValue, "Invalid length on ArrayValue during reconcilation");
let length = lengthValue.value;
for (let i = 0; i < length; i++) {
let elementProperty = array.properties.get("" + i);
let elementPropertyDescriptor = elementProperty && elementProperty.descriptor;
(0, _invariant2.default)(elementPropertyDescriptor, `Invalid ArrayValue[${i}] descriptor`);
let elementValue = elementPropertyDescriptor.value;
if (elementValue instanceof _index.Value) {
mapFunc(elementValue, elementPropertyDescriptor);
}
}
}
function GetDescriptorForProperty(value, propertyName) {
let object = value.properties.get(propertyName);
(0, _invariant2.default)(object);
return object.descriptor;
}
function convertSimpleClassComponentToFunctionalComponent(realm, complexComponentType, additionalFunctionEffects) {
let prototype = complexComponentType.properties.get("prototype");
(0, _invariant2.default)(prototype);
(0, _invariant2.default)(prototype.descriptor);
prototype.descriptor.configurable = true;
_singletons.Properties.DeletePropertyOrThrow(realm, complexComponentType, "prototype");
// fix the length as we've changed the arguments
let lengthProperty = GetDescriptorForProperty(complexComponentType, "length");
(0, _invariant2.default)(lengthProperty);
lengthProperty.writable = false;
lengthProperty.enumerable = false;
lengthProperty.configurable = true;
// ensure the length value is set to the new value
let lengthValue = (0, _index2.Get)(realm, complexComponentType, "length");
(0, _invariant2.default)(lengthValue instanceof _index.NumberValue);
lengthValue.value = 2;
// change the function kind
complexComponentType.$FunctionKind = "normal";
// set the prototype back to an object
complexComponentType.$Prototype = realm.intrinsics.FunctionPrototype;
// give the function the functional components params
complexComponentType.$FormalParameters = [t.identifier("props"), t.identifier("context")];
// add a transform to occur after the additional function has serialized the body of the class
additionalFunctionEffects.transforms.push(body => {
// as this was a class before and is now a functional component, we need to replace
// this.props and this.context to props and context, via the function arugments
let funcNode = t.functionExpression(null, [], t.blockStatement(body));
(0, _babelTraverse2.default)(t.file(t.program([t.expressionStatement(funcNode)])), {
"Identifier|ThisExpression"(path) {
let node = path.node;
if (t.isIdentifier(node) && node.name === "this" || t.isThisExpression(node)) {
let parentPath = path.parentPath;
let parentNode = parentPath.node;
if (t.isMemberExpression(parentNode)) {
// remove the "this" from the member
parentPath.replaceWith(parentNode.property);
} else {
throw new _errors.FatalError(`conversion of a simple class component to functional component failed due to "this" not being replaced`);
}
}
}
}, undefined, undefined, undefined);
});
}
function createBinding(descriptor, key, object) {
return {
descriptor,
key,
object
};
}
function cloneProperties(realm, properties, object) {
let newProperties = new Map();
for (let [propertyName, { descriptor }] of properties) {
newProperties.set(propertyName, createBinding((0, _index2.cloneDescriptor)(descriptor), propertyName, object));
}
return newProperties;
}
function cloneSymbols(realm, symbols, object) {
let newSymbols = new Map();
for (let [symbol, { descriptor }] of symbols) {
newSymbols.set(symbol, createBinding((0, _index2.cloneDescriptor)(descriptor), symbol, object));
}
return newSymbols;
}
function cloneValue(realm, originalValue, _prototype, copyToObject) {
if (originalValue instanceof _index.FunctionValue) {
return cloneFunction(realm, originalValue, _prototype, copyToObject);
}
(0, _invariant2.default)(false, "TODO: add support to cloneValue() for more value types");
}
function cloneFunction(realm, originalValue, _prototype, copyToObject) {
let newValue;
if (originalValue instanceof _index.ECMAScriptSourceFunctionValue) {
newValue = copyToObject || new _index.ECMAScriptSourceFunctionValue(realm, originalValue.intrinsicName);
(0, _invariant2.default)(newValue instanceof _index.ECMAScriptSourceFunctionValue);
// $FlowFixMe: complains about Object.assign
Object.assign(newValue, originalValue);
let properties = cloneProperties(realm, originalValue.properties, newValue);
newValue.properties = properties;
let symbols = cloneSymbols(realm, originalValue.symbols, newValue);
newValue.symbols = symbols;
// handle home object + prototype
let originalPrototype = originalValue.$HomeObject;
(0, _invariant2.default)(originalPrototype instanceof _index.ObjectValue);
let prototype = _prototype || clonePrototype(realm, originalPrototype);
newValue.$HomeObject = prototype;
if (originalPrototype.properties.has("constructor")) {
_singletons.Properties.Set(realm, prototype, "constructor", newValue, false);
}
if (originalValue.properties.has("prototype")) {
_singletons.Properties.Set(realm, newValue, "prototype", prototype, false);
}
}
(0, _invariant2.default)(newValue instanceof _index.FunctionValue, "TODO: add support to cloneValue() for more function types");
return newValue;
}
function clonePrototype(realm, prototype) {
(0, _invariant2.default)(prototype instanceof _index.ObjectValue);
let newPrototype = new _index.ObjectValue(realm, realm.intrinsics.ObjectPrototype, prototype.intrinsicName);
Object.assign(newPrototype, prototype);
for (let [propertyName] of prototype.properties) {
if (propertyName !== "constructor") {
let originalValue = (0, _index2.Get)(realm, prototype, propertyName);
let newValue = cloneValue(realm, originalValue, prototype);
_singletons.Properties.Set(realm, newPrototype, propertyName, newValue, false);
}
}
for (let [symbol] of prototype.symbols) {
let originalValue = (0, _index2.Get)(realm, prototype, symbol);
let newValue = cloneValue(realm, originalValue, prototype);
_singletons.Properties.Set(realm, newPrototype, symbol, newValue, false);
}
return newPrototype;
}
const skipFunctionProperties = new Set(["length", "prototype", "arguments", "name", "caller"]);
function convertFunctionalComponentToComplexClassComponent(realm, functionalComponentType, complexComponentType, additionalFunctionEffects) {
(0, _invariant2.default)(complexComponentType instanceof _index.ECMAScriptSourceFunctionValue);
// get all properties on the functional component that were added in user-code
// we add defaultProps as undefined, as merging a class component's defaultProps on to
// a differnet component isn't right, we can discard defaultProps instead via folding
// we also don't want propTypes from the class component, so we remove that too
let userCodePropertiesToAdd = new Map([["defaultProps", createBinding(undefined, "defaultProps", functionalComponentType)], ["propTypes", createBinding(undefined, "propTypes", functionalComponentType)]]);
let userCodeSymbolsToAdd = new Map();
for (let [propertyName, binding] of functionalComponentType.properties) {
if (!skipFunctionProperties.has(propertyName)) {
userCodePropertiesToAdd.set(propertyName, binding);
}
}
for (let [symbol, binding] of functionalComponentType.symbols) {
userCodeSymbolsToAdd.set(symbol, binding);
}
cloneValue(realm, complexComponentType, null, functionalComponentType);
// then copy back and properties that were on the original functional component
// ensuring we overwrite any existing ones
for (let [propertyName, binding] of userCodePropertiesToAdd) {
functionalComponentType.properties.set(propertyName, binding);
}
for (let [symbol, binding] of userCodeSymbolsToAdd) {
functionalComponentType.symbols.set(symbol, binding);
}
// add a transform to occur after the additional function has serialized the body of the class
additionalFunctionEffects.transforms.push(body => {
// as we've converted a functional component to a complex one, we are going to have issues with
// "props" and "context" references, as they're now going to be "this.props" and "this.context".
// we simply need a to add to vars to beginning of the body to get around this
// if they're not used, any DCE tool post-Prepack (GCC or Uglify) will remove them
body.unshift(t.variableDeclaration("var", [t.variableDeclarator(t.identifier("props"), t.memberExpression(t.thisExpression(), t.identifier("props"))), t.variableDeclarator(t.identifier("context"), t.memberExpression(t.thisExpression(), t.identifier("context")))]));
});
}
function normalizeFunctionalComponentParamaters(func) {
func.$FormalParameters = func.$FormalParameters.map((param, i) => {
if (i === 0) {
return t.isIdentifier(param) ? param : t.identifier("props");
} else {
return t.isIdentifier(param) ? param : t.identifier("context");
}
});
}
function createReactHintObject(object, propertyName, args) {
return {
object,
propertyName,
args
};
}
function getComponentTypeFromRootValue(realm, value) {
let _valueIsKnownReactAbstraction = valueIsKnownReactAbstraction(realm, value);
if (!(value instanceof _index.ECMAScriptSourceFunctionValue || _valueIsKnownReactAbstraction)) {
return null;
}
if (_valueIsKnownReactAbstraction) {
(0, _invariant2.default)(value instanceof _AbstractValue2.default);
let reactHint = realm.react.abstractHints.get(value);
(0, _invariant2.default)(reactHint);
if (typeof reactHint !== "string" && reactHint.object === realm.fbLibraries.reactRelay) {
switch (reactHint.propertyName) {
case "createFragmentContainer":
case "createPaginationContainer":
case "createRefetchContainer":
(0, _invariant2.default)(Array.isArray(reactHint.args));
// componentType is the 1st argument of a ReactRelay container
let componentType = reactHint.args[0];
(0, _invariant2.default)(componentType instanceof _index.ECMAScriptSourceFunctionValue);
return componentType;
default:
(0, _invariant2.default)(false, `unsupported known React abstraction - ReactRelay property "${reactHint.propertyName}" not supported`);
}
}
(0, _invariant2.default)(false, "unsupported known React abstraction");
} else {
(0, _invariant2.default)(value instanceof _index.ECMAScriptSourceFunctionValue);
return value;
}
}
// props should never have "ref" or "key" properties, as they're part of ReactElement
// object instead. to ensure that we can give this hint, we create them and then
// delete them, so their descriptor is left undefined. we use this knowledge later
// to ensure that when dealing with creating ReactElements with partial config,
// we don't have to bail out becuase "config" may or may not have "key" or/and "ref"
function deleteRefAndKeyFromProps(realm, props) {
let objectValue;
if (props instanceof _index.AbstractObjectValue) {
let elements = props.values.getElements();
if (elements && elements.size > 0) {
objectValue = Array.from(elements)[0];
}
// we don't want to serialize in the output that we're making these deletes
(0, _invariant2.default)(objectValue instanceof _index.ObjectValue);
objectValue.refuseSerialization = true;
}
_singletons.Properties.Set(realm, props, "ref", realm.intrinsics.undefined, true);
props.$Delete("ref");
_singletons.Properties.Set(realm, props, "key", realm.intrinsics.undefined, true);
props.$Delete("key");
if (props instanceof _index.AbstractObjectValue) {
(0, _invariant2.default)(objectValue instanceof _index.ObjectValue);
objectValue.refuseSerialization = false;
}
}
function objectHasNoPartialKeyAndRef(realm, object) {
if (object instanceof _AbstractValue2.default) {
return true;
}
return !((0, _index2.Get)(realm, object, "key") instanceof _AbstractValue2.default || (0, _index2.Get)(realm, object, "ref") instanceof _AbstractValue2.default);
}
function recursivelyFlattenArray(realm, array, targetArray) {
forEachArrayValue(realm, array, item => {
if (item instanceof _index.ArrayValue) {
recursivelyFlattenArray(realm, item, targetArray);
} else {
let lengthValue = (0, _index2.Get)(realm, targetArray, "length");
(0, _invariant2.default)(lengthValue instanceof _index.NumberValue);
_singletons.Properties.Set(realm, targetArray, "" + lengthValue.value, item, true);
}
});
}
function flattenChildren(realm, array) {
let flattenedChildren = _singletons.Create.ArrayCreate(realm, 0);
recursivelyFlattenArray(realm, array, flattenedChildren);
return flattenedChildren;
}
function evaluateComponentTreeBranch(realm, effects, nested, f) {
let [value, generator, modifiedBindings, modifiedProperties, createdObjects] = effects;
if (nested) {
realm.applyEffects([value, new _generator.Generator(realm, "evaluateComponentTreeBranch"), modifiedBindings, modifiedProperties, createdObjects]);
}
try {
return f(generator, value);
} finally {
if (nested) {
realm.restoreBindings(modifiedBindings);
realm.restoreProperties(modifiedProperties);
}
}
}
function setProperty(realm, object, property, value) {
if (object instanceof _index.AbstractObjectValue) {
let elements = object.values.getElements();
if (elements && elements.size > 0) {
object = Array.from(elements)[0];
}
(0, _invariant2.default)(object instanceof _index.ObjectValue);
}
let binding;
if (typeof property === "string") {
binding = object.properties.get(property);
} else {
binding = object.symbols.get(property);
}
if (!binding) {
return;
}
let descriptor = binding.descriptor;
if (!descriptor) {
return;
}
descriptor.value = value;
}
function getProperty(realm, object, property) {
if (object instanceof _index.AbstractObjectValue) {
let elements = object.values.getElements();
if (elements && elements.size > 0) {
object = Array.from(elements)[0];
}
(0, _invariant2.default)(object instanceof _index.ObjectValue);
}
let binding;
if (typeof property === "string") {
binding = object.properties.get(property);
} else {
binding = object.symbols.get(property);
}
if (!binding) {
return realm.intrinsics.undefined;
}
let descriptor = binding.descriptor;
if (!descriptor) {
return realm.intrinsics.undefined;
}
let value;
if (descriptor.value) {
value = descriptor.value;
} else if (descriptor.get || descriptor.set) {
_AbstractValue2.default.reportIntrospectionError(object, `react/utils/getProperty unsupported getter/setter property`);
throw new _errors.FatalError();
}
(0, _invariant2.default)(value instanceof _index.Value, `react/utils/getProperty should not be called on internal properties`);
return value;
}
function visitName(path, state, name, read, write) {
// Is the name bound to some local identifier? If so, we don't need to do anything
if (path.scope.hasBinding(name, /*noGlobals*/true)) return;
// Otherwise, let's record that there's an unbound identifier
if (read) state.unboundReads.add(name);
if (write) state.unboundWrites.add(name);
}
function ignorePath(path) {
let parent = path.parent;
return t.isLabeledStatement(parent) || t.isBreakStatement(parent) || t.isContinueStatement(parent);
}
let LeakedClosureRefVisitor = {
ReferencedIdentifier(path, state) {
if (ignorePath(path)) return;
let innerName = path.node.name;
if (innerName === "arguments") {
return;
}
visitName(path, state, innerName, true, false);
},
"AssignmentExpression|UpdateExpression"(path, state) {
let doesRead = path.node.operator !== "=";
for (let name in path.getBindingIdentifiers()) {
visitName(path, state, name, doesRead, true);
}
}
};
function getFunctionBindingInfo(value) {
(0, _invariant2.default)(value instanceof _index.ECMAScriptSourceFunctionValue);
(0, _invariant2.default)(value.constructor === _index.ECMAScriptSourceFunctionValue);
let functionInfo = {
unboundReads: new Set(),
unboundWrites: new Set()
};
let formalParameters = value.$FormalParameters;
(0, _invariant2.default)(formalParameters != null);
let code = value.$ECMAScriptCode;
(0, _invariant2.default)(code != null);
(0, _babelTraverse2.default)(t.file(t.program([t.expressionStatement(t.functionExpression(null, formalParameters, code))])), LeakedClosureRefVisitor, null, functionInfo);
return functionInfo;
}
// if a render prop function (a nested additional function) makes
// no accesses to bindings in the parent additional function scope
// we can determine if the function is self contained
function isRenderPropFunctionSelfContained(realm, parentFunc, renderProp, logger // otherwise Flow cycles increases
) {
let { unboundReads, unboundWrites } = getFunctionBindingInfo(renderProp);
let bindings = Array.from(unboundReads).concat(Array.from(unboundWrites));
for (let name of bindings) {
let reference = logger.tryQuery(() => _singletons.Environment.ResolveBinding(realm, name, true, renderProp.$Environment), undefined);
if (!reference) {
return false;
}
if (reference.base instanceof _environment.FunctionEnvironmentRecord && reference.base.$FunctionObject === parentFunc) {
return false;
}
}
return true;
}
function createReactEvaluatedNode(status, name) {
return {
children: [],
message: "",
name,
status
};
}
function getComponentName(realm, componentType) {
(0, _invariant2.default)(componentType instanceof _index.ECMAScriptSourceFunctionValue || componentType instanceof _index.AbstractObjectValue || componentType instanceof _AbstractValue2.default);
if (componentType.__originalName) {
return componentType.__originalName;
}
if (realm.fbLibraries.reactRelay !== undefined) {
if (componentType === (0, _index2.Get)(realm, realm.fbLibraries.reactRelay, "QueryRenderer")) {
return "QueryRenderer";
}
}
if (componentType instanceof _index.ECMAScriptSourceFunctionValue) {
let name = (0, _index2.Get)(realm, componentType, "name");
if (name instanceof _index.StringValue) {
return name.value;
}
}
return "Unknown";
}
function convertConfigObjectToReactComponentTreeConfig(realm, config) {
// defaults
let firstRenderOnly = false;
if (!(config instanceof _index.UndefinedValue)) {
for (let [key] of config.properties) {
let propValue = getProperty(realm, config, key);
if (propValue instanceof _index.StringValue || propValue instanceof _index.NumberValue || propValue instanceof _index.BooleanValue) {
let value = propValue.value;
// boolean options
if (typeof value === "boolean") {
if (key === "firstRenderOnly") {
firstRenderOnly = value;
}
}
} else {
let diagnostic = new _errors.CompilerDiagnostic("__optimizeReactComponentTree(rootComponent, config) has been called with invalid arguments", realm.currentLocation, "PP0024", "FatalError");
realm.handleError(diagnostic);
if (realm.handleError(diagnostic) === "Fail") throw new _errors.FatalError();
}
}
}
return {
firstRenderOnly
};
}
function getValueFromRenderCall(realm, renderFunction, instance, args, componentTreeConfig) {
(0, _invariant2.default)(renderFunction.$Call, "Expected render function to be a FunctionValue with $Call method");
let funcCall = renderFunction.$Call;
let effects;
try {
effects = realm.evaluateForEffects(() => funcCall(instance, args), null, "component render");
} catch (error) {
throw error;
}
let completion = effects[0];
if (completion instanceof _completions.PossiblyNormalCompletion) {
// in this case one of the branches may complete abruptly, which means that
// not all control flow branches join into one flow at this point.
// Consequently we have to continue tracking changes until the point where
// all the branches come together into one.
completion = realm.composeWithSavedCompletion(completion);
}
// the render method doesn't have any arguments, so we just assign the context of "this" to be the instance
if (componentTreeConfig.firstRenderOnly && completion instanceof _AbstractValue2.default && completion.args.length === 0) {
throw new _errors2.ExpectedBailOut("completely unknown component render currently supported on first render");
}
// Note that the effects of (non joining) abrupt branches are not included
// in joinedEffects, but are tracked separately inside completion.
realm.applyEffects(effects);
// return or throw completion
if (completion instanceof _completions.AbruptCompletion) throw completion;
(0, _invariant2.default)(completion instanceof _index.Value);
return completion;
}
function isEventProp(name) {
return name.length > 2 && name[0].toLowerCase() === "o" && name[1].toLowerCase() === "n";
}
function sanitizeReactElementForFirstRenderOnly(realm, reactElement) {
let typeValue = (0, _index2.Get)(realm, reactElement, "type");
// ensure ref is null, as we don't use that on first render
setProperty(realm, reactElement, "ref", realm.intrinsics.null);
// when dealing with host nodes, we want to sanitize them futher
if (typeValue instanceof _index.StringValue) {
let propsValue = (0, _index2.Get)(realm, reactElement, "props");
if (propsValue instanceof _index.ObjectValue) {
// remove all values apart from string/number/boolean
for (let [propName] of propsValue.properties) {
// check for onSomething prop event handlers, i.e. onClick
if (isEventProp(propName)) {
propsValue.$Delete(propName);
}
}
}
}
return reactElement;
}
//# sourceMappingURL=utils.js.map