"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ResidualReactElements = undefined; var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); /** * 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. */ var _realm = require("../realm.js"); var _ResidualHeapSerializer = require("./ResidualHeapSerializer.js"); var _hoisting = require("../react/hoisting.js"); var _index = require("../methods/index.js"); var _babelTypes = require("babel-types"); var t = _interopRequireWildcard(_babelTypes); var _index2 = require("../values/index.js"); var _jsx = require("../react/jsx.js"); var _logger = require("./logger.js"); var _invariant = require("../invariant.js"); var _invariant2 = _interopRequireDefault(_invariant); var _errors = require("../errors"); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 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 _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } var ResidualReactElements = exports.ResidualReactElements = function () { function ResidualReactElements(realm, residualHeapSerializer) { _classCallCheck(this, ResidualReactElements); this.realm = realm; this.residualHeapSerializer = residualHeapSerializer; this.logger = residualHeapSerializer.logger; this.reactOutput = realm.react.output || "create-element"; this._lazilyHoistedNodes = undefined; } _createClass(ResidualReactElements, [{ key: "serializeReactElement", value: function serializeReactElement(val) { var typeValue = (0, _index.Get)(this.realm, val, "type"); var keyValue = (0, _index.Get)(this.realm, val, "key"); var refValue = (0, _index.Get)(this.realm, val, "ref"); var propsValue = (0, _index.Get)(this.realm, val, "props"); (0, _invariant2.default)(typeValue !== null, "ReactElement type of null"); var attributes = []; var children = []; if (keyValue !== null) { var keyExpr = this.residualHeapSerializer.serializeValue(keyValue); if (keyExpr.type !== "NullLiteral") { if (this.reactOutput === "jsx") { this._addSerializedValueToJSXAttriutes("key", keyExpr, attributes); } else if (this.reactOutput === "create-element") { this._addSerializedValueToObjectProperty("key", keyExpr, attributes); } } } if (refValue !== null) { var refExpr = this.residualHeapSerializer.serializeValue(refValue); if (refExpr.type !== "NullLiteral") { if (this.reactOutput === "jsx") { this._addSerializedValueToJSXAttriutes("ref", refExpr, attributes); } else if (this.reactOutput === "create-element") { this._addSerializedValueToObjectProperty("ref", refExpr, attributes); } } } if (propsValue instanceof _index2.ObjectValue) { // the propsValue is visited to get the properties, but we don't emit it as the object this.residualHeapSerializer.serializedValues.add(propsValue); // have to case propsValue to ObjectValue or Flow complains that propsValues can be null/undefined var _iteratorNormalCompletion = true; var _didIteratorError = false; var _iteratorError = undefined; try { for (var _iterator = propsValue.properties[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { var _ref = _step.value; var _ref2 = _slicedToArray(_ref, 2); var key = _ref2[0]; var propertyBinding = _ref2[1]; var desc = propertyBinding.descriptor; if (desc === undefined) continue; // deleted (0, _invariant2.default)(!(0, _index.IsAccessorDescriptor)(this.realm, desc), "expected descriptor to be a non-accessor property"); (0, _invariant2.default)(key !== "key" && key !== "ref", "\"" + key + "\" is a reserved prop name"); if (key === "children" && desc.value !== undefined) { var childrenValue = desc.value; if (childrenValue instanceof _index2.ArrayValue) { this.residualHeapSerializer.serializedValues.add(childrenValue); var childrenLength = (0, _index.Get)(this.realm, childrenValue, "length"); var childrenLengthValue = 0; if (childrenLength instanceof _index2.NumberValue) { childrenLengthValue = childrenLength.value; for (var i = 0; i < childrenLengthValue; i++) { var child = (0, _index.Get)(this.realm, childrenValue, "" + i); if (child instanceof _index2.Value) { children.push(this._serializeReactElementChild(child)); } else { this.logger.logError(val, "ReactElement \"props.children[" + i + "]\" failed to serialize due to a non-value"); } } continue; } } // otherwise it must be a value, as desc.value !== undefined. children.push(this._serializeReactElementChild(childrenValue)); continue; } if (desc.value instanceof _index2.Value) { if (this.reactOutput === "jsx") { this._addSerializedValueToJSXAttriutes(key, this.residualHeapSerializer.serializeValue(desc.value), attributes); } else if (this.reactOutput === "create-element") { this._addSerializedValueToObjectProperty(key, this.residualHeapSerializer.serializeValue(desc.value), attributes); } } } } catch (err) { _didIteratorError = true; _iteratorError = err; } finally { try { if (!_iteratorNormalCompletion && _iterator.return) { _iterator.return(); } } finally { if (_didIteratorError) { throw _iteratorError; } } } } var reactLibraryObject = this.realm.react.reactLibraryObject; var shouldHoist = this.residualHeapSerializer.currentFunctionBody !== this.residualHeapSerializer.mainBody && (0, _hoisting.canHoistReactElement)(this.realm, val); var id = this.residualHeapSerializer.getSerializeObjectIdentifier(val); // this identifier is used as the deafult, but also passed to the hoisted factory function var originalCreateElementIdentifier = null; // this name is used when hoisting, and is passed into the factory function, rather than the original var hoistedCreateElementIdentifier = null; var reactElement = void 0; if (this.reactOutput === "jsx") { reactElement = this._serializeReactElementToJSXElement(val, typeValue, attributes, children, reactLibraryObject); } else if (this.reactOutput === "create-element") { // if there is no React library, then we should throw and error, as it is needed for createElement output if (reactLibraryObject === undefined) { throw new _errors.FatalError("unable to serialize JSX to createElement due to React not being referenced in scope"); } var createElement = (0, _index.Get)(this.realm, reactLibraryObject, "createElement"); originalCreateElementIdentifier = this.residualHeapSerializer.serializeValue(createElement); if (shouldHoist) { // if we haven't created a _lazilyHoistedNodes before, then this is the first time // so we only create the hoisted identifier once if (this._lazilyHoistedNodes === undefined) { // create a new unique instance hoistedCreateElementIdentifier = t.identifier(this.residualHeapSerializer.intrinsicNameGenerator.generate()); } else { hoistedCreateElementIdentifier = this._lazilyHoistedNodes.createElementIdentifier; } } reactElement = this._serializeReactElementToCreateElement(val, typeValue, attributes, children, shouldHoist ? hoistedCreateElementIdentifier : originalCreateElementIdentifier); } else { (0, _invariant2.default)(false, "Unknown reactOutput specified"); } // if we are hoisting this React element, put the assignment in the body // also ensure we are in an additional function if (shouldHoist) { // if the currentHoistedReactElements is not defined, we create it an emit the function call // this should only occur once per additional function if (this._lazilyHoistedNodes === undefined) { var funcId = t.identifier(this.residualHeapSerializer.functionNameGenerator.generate()); this._lazilyHoistedNodes = { id: funcId, createElementIdentifier: hoistedCreateElementIdentifier, nodes: [] }; var statement = t.expressionStatement(t.logicalExpression("&&", t.binaryExpression("===", id, t.unaryExpression("void", t.numericLiteral(0), true)), // pass the createElementIdentifier if it's not null t.callExpression(funcId, originalCreateElementIdentifier ? [originalCreateElementIdentifier] : []))); this.residualHeapSerializer.emitter.emit(statement); } // we then push the reactElement and its id into our list of elements to process after // the current additional function has serialzied (0, _invariant2.default)(this._lazilyHoistedNodes !== undefined); (0, _invariant2.default)(Array.isArray(this._lazilyHoistedNodes.nodes)); this._lazilyHoistedNodes.nodes.push({ id: id, astNode: reactElement }); } else { var declar = t.variableDeclaration("var", [t.variableDeclarator(id, reactElement)]); this.residualHeapSerializer.emitter.emit(declar); } return reactElement; } }, { key: "_addSerializedValueToJSXAttriutes", value: function _addSerializedValueToJSXAttriutes(prop, expr, attributes) { attributes.push((0, _jsx.convertKeyValueToJSXAttribute)(prop, expr)); } }, { key: "_addSerializedValueToObjectProperty", value: function _addSerializedValueToObjectProperty(prop, expr, attributes) { var key = void 0; if (prop.includes("-")) { key = t.stringLiteral(prop); } else { key = t.identifier(prop); } attributes.push(t.objectProperty(key, expr)); } }, { key: "_serializeReactElementToCreateElement", value: function _serializeReactElementToCreateElement(val, typeValue, attributes, children, createElementIdentifier) { var typeIdentifier = this.residualHeapSerializer.serializeValue(typeValue); var createElementArguments = [typeIdentifier]; // check if we need to add attributes if (attributes.length !== 0) { // cast to any for createElementArguments as casting it to BabelNodeObjectProperty[] isn't working createElementArguments.push(t.objectExpression(attributes)); } if (children.length !== 0) { if (attributes.length === 0) { createElementArguments.push(t.nullLiteral()); } createElementArguments.push.apply(createElementArguments, _toConsumableArray(children)); } // cast to any for createElementArguments as casting it to BabelNodeExpresion[] isn't working var createElementCall = t.callExpression(createElementIdentifier, createElementArguments); this._addBailOutMessageToBabelNode(val, createElementCall); return createElementCall; } }, { key: "_serializeReactElementToJSXElement", value: function _serializeReactElementToJSXElement(val, typeValue, attributes, children, reactLibraryObject) { if (reactLibraryObject !== undefined) { this.residualHeapSerializer.serializeValue(reactLibraryObject); } var identifier = (0, _jsx.convertExpressionToJSXIdentifier)(this.residualHeapSerializer.serializeValue(typeValue), true); var openingElement = t.jSXOpeningElement(identifier, attributes, children.length === 0); var closingElement = t.jSXClosingElement(identifier); var jsxElement = t.jSXElement(openingElement, closingElement, children, children.length === 0); this._addBailOutMessageToBabelNode(val, jsxElement); return jsxElement; } }, { key: "_addBailOutMessageToBabelNode", value: function _addBailOutMessageToBabelNode(val, node) { // if there has been a bail-out, we create an inline BlockComment node before the JSX element if (val.$BailOutReason !== undefined) { // $BailOutReason contains an optional string of what to print out in the comment node.leadingComments = [{ type: "BlockComment", value: "" + val.$BailOutReason }]; } } }, { key: "_serializeReactElementChild", value: function _serializeReactElementChild(child) { var expr = this.residualHeapSerializer.serializeValue(child); if (this.reactOutput === "jsx") { if (t.isStringLiteral(expr) || t.isNumericLiteral(expr)) { return t.jSXText(expr.value + ""); } else if (t.isJSXElement(expr)) { return expr; } return t.jSXExpressionContainer(expr); } else if (this.reactOutput === "create-element") { return expr; } (0, _invariant2.default)(false, "Unknown reactOutput specified"); } }, { key: "serializeLazyHoistedNodes", value: function serializeLazyHoistedNodes() { var entries = []; if (this._lazilyHoistedNodes !== undefined) { var _lazilyHoistedNodes = this._lazilyHoistedNodes, id = _lazilyHoistedNodes.id, nodes = _lazilyHoistedNodes.nodes, createElementIdentifier = _lazilyHoistedNodes.createElementIdentifier; // create a function that initializes all the hoisted nodes var func = t.functionExpression(null, // use createElementIdentifier if it's not null createElementIdentifier ? [createElementIdentifier] : [], t.blockStatement(nodes.map(function (node) { return t.expressionStatement(t.assignmentExpression("=", node.id, node.astNode)); }))); // push it to the mainBody of the module entries.push(t.variableDeclaration("var", [t.variableDeclarator(id, func)])); // output all the empty variable declarations that will hold the nodes lazily entries.push.apply(entries, _toConsumableArray(nodes.map(function (node) { return t.variableDeclaration("var", [t.variableDeclarator(node.id)]); }))); // reset the _lazilyHoistedNodes so other additional functions work this._lazilyHoistedNodes = undefined; } return entries; } }]); return ResidualReactElements; }(); //# sourceMappingURL=ResidualReactElements.js.map