UNPKG

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
"use strict"; 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