UNPKG

prepack

Version:

Execute a JS bundle, serialize global state and side effects to a snapshot that can be quickly restored.

230 lines (182 loc) 9.58 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ResidualReactElementVisitor = void 0; var _realm = require("../realm.js"); var _index = require("../values/index.js"); var _ResidualHeapVisitor = require("./ResidualHeapVisitor.js"); var _hoisting = require("../react/hoisting.js"); var _elements = require("../react/elements.js"); var _utils = require("../react/utils.js"); var _invariant = _interopRequireDefault(require("../invariant.js")); var _generator = require("../utils/generator.js"); var _ReactEquivalenceSet = require("../react/ReactEquivalenceSet.js"); var _ReactElementSet = require("../react/ReactElementSet.js"); var _ReactPropsSet = require("../react/ReactPropsSet.js"); var _index2 = require("../methods/index.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 */ class ResidualReactElementVisitor { constructor(realm, residualHeapVisitor) { this.realm = realm; this.residualHeapVisitor = residualHeapVisitor; this.reactOutput = realm.react.output || "create-element"; this.defaultEquivalenceSet = true; this.reactEquivalenceSet = new _ReactEquivalenceSet.ReactEquivalenceSet(realm, this); this.reactElementEquivalenceSet = new _ReactElementSet.ReactElementSet(realm, this.reactEquivalenceSet); this.reactPropsEquivalenceSet = new _ReactPropsSet.ReactPropsSet(realm, this.reactEquivalenceSet); } visitReactElement(reactElement) { let reactElementData = this.realm.react.reactElements.get(reactElement); (0, _invariant.default)(reactElementData !== undefined); let { firstRenderOnly } = reactElementData; let isReactFragment = false; (0, _elements.traverseReactElement)(this.realm, reactElement, { visitType: typeValue => { let reactElementStringTypeReferences = this.realm.react.reactElementStringTypeReferences; // If the type is a text value, and we have a derived reference for it // then use that derived reference instead of the string value. This is // primarily designed around RCTView and RCTText, which are string values // for RN apps, but are treated as special host components. if (typeValue instanceof _index.StringValue && reactElementStringTypeReferences.has(typeValue.value)) { let reference = reactElementStringTypeReferences.get(typeValue.value); (0, _invariant.default)(reference instanceof _index.AbstractValue); (0, _utils.hardModifyReactObjectPropertyBinding)(this.realm, reactElement, "type", reference); this.residualHeapVisitor.visitValue(reference); return; } isReactFragment = typeValue instanceof _index.SymbolValue && typeValue === (0, _utils.getReactSymbol)("react.fragment", this.realm); // we don't want to visit fragments as they are internal values if (!isReactFragment) { this.residualHeapVisitor.visitValue(typeValue); } }, visitKey: keyValue => { this.residualHeapVisitor.visitValue(keyValue); }, visitRef: refValue => { if (!firstRenderOnly) { this.residualHeapVisitor.visitValue(refValue); } }, visitAbstractOrPartialProps: propsValue => { this.residualHeapVisitor.visitValue(propsValue); }, visitConcreteProps: propsValue => { for (let [propName, binding] of propsValue.properties) { (0, _invariant.default)(propName !== "key" && propName !== "ref", `"${propName}" is a reserved prop name`); if (binding.descriptor === undefined || propName === "children") { continue; } let propValue = (0, _utils.getProperty)(this.realm, propsValue, propName); if ((0, _utils.canExcludeReactElementObjectProperty)(this.realm, reactElement, propName, propValue)) { continue; } this.residualHeapVisitor.visitValue(propValue); } }, visitChildNode: childValue => { this.residualHeapVisitor.visitValue(childValue); } }); // Our serializer requires that every value we serialize must first be visited in every scope where it appears. In // our React element serializer we serialize some values (namely `React.createElement` and `React.Fragment`) that do // not necessarily appear in our source code. We must manually visit these values in our visitor pass for the values // to be serializable. if (this.realm.react.output === "create-element") { const reactLibraryObject = this._getReactLibraryValue(); (0, _invariant.default)(reactLibraryObject instanceof _index.ObjectValue); const createElement = reactLibraryObject.properties.get("createElement"); (0, _invariant.default)(createElement !== undefined); const reactCreateElement = (0, _index2.Get)(this.realm, reactLibraryObject, "createElement"); // Our `createElement` value will be used in the prelude of the optimized function we serialize to initialize // our hoisted React elements. So we need to ensure that we visit our value in a scope above our own to allow // the function to be used in our optimized function prelude. We use our global scope to accomplish this. We are // a "friend" class of `ResidualHeapVisitor` so we call one of its private methods. this.residualHeapVisitor._visitInUnrelatedScope(this.residualHeapVisitor.globalGenerator, reactCreateElement); } if (isReactFragment) { const reactLibraryObject = this._getReactLibraryValue(); // Our `React.Fragment` value will be used in the function to lazily initialize hoisted JSX elements. So we need // to visit the library in our global generator so that it is available when creating the hoisted elements. this.residualHeapVisitor._visitInUnrelatedScope(this.residualHeapVisitor.globalGenerator, reactLibraryObject); } // determine if this ReactElement node tree is going to be hoistable (0, _hoisting.determineIfReactElementCanBeHoisted)(this.realm, reactElement, this.residualHeapVisitor); } withCleanEquivalenceSet(func) { let defaultEquivalenceSet = this.defaultEquivalenceSet; let reactEquivalenceSet = this.reactEquivalenceSet; let reactElementEquivalenceSet = this.reactElementEquivalenceSet; let reactPropsEquivalenceSet = this.reactPropsEquivalenceSet; this.defaultEquivalenceSet = false; this.reactEquivalenceSet = new _ReactEquivalenceSet.ReactEquivalenceSet(this.realm, this); this.reactElementEquivalenceSet = new _ReactElementSet.ReactElementSet(this.realm, this.reactEquivalenceSet); this.reactPropsEquivalenceSet = new _ReactPropsSet.ReactPropsSet(this.realm, this.reactEquivalenceSet); func(); // Cleanup this.defaultEquivalenceSet = defaultEquivalenceSet; this.reactEquivalenceSet = reactEquivalenceSet; this.reactElementEquivalenceSet = reactElementEquivalenceSet; this.reactPropsEquivalenceSet = reactPropsEquivalenceSet; } saveEquivalenceSet() { const { reactEquivalenceSet, reactElementEquivalenceSet, reactPropsEquivalenceSet } = this; return { reactEquivalenceSet, reactElementEquivalenceSet, reactPropsEquivalenceSet }; } loadEquivalenceSet(save, func) { const defaultEquivalenceSet = this.defaultEquivalenceSet; const reactEquivalenceSet = this.reactEquivalenceSet; const reactElementEquivalenceSet = this.reactElementEquivalenceSet; const reactPropsEquivalenceSet = this.reactPropsEquivalenceSet; this.defaultEquivalenceSet = false; this.reactEquivalenceSet = save.reactEquivalenceSet; this.reactElementEquivalenceSet = save.reactElementEquivalenceSet; this.reactPropsEquivalenceSet = save.reactPropsEquivalenceSet; const result = func(); // Cleanup this.defaultEquivalenceSet = defaultEquivalenceSet; this.reactEquivalenceSet = reactEquivalenceSet; this.reactElementEquivalenceSet = reactElementEquivalenceSet; this.reactPropsEquivalenceSet = reactPropsEquivalenceSet; return result; } wasTemporalAliasDeclaredInCurrentScope(temporalAlias) { let scope = this.residualHeapVisitor.scope; if (scope instanceof _index.FunctionValue) { return false; } // If the temporal has already been visited, then we know the temporal // value was used and thus declared in another scope if (this.residualHeapVisitor.values.has(temporalAlias)) { return false; } // Otherwise, we check the current scope and see if the // temporal value was declared in one of the entries for (let i = 0; i < scope._entries.length; i++) { let entry = scope._entries[i]; if (entry instanceof _generator.TemporalOperationEntry) { if (entry.declared === temporalAlias) { return true; } } } return false; } _getReactLibraryValue() { const reactLibraryObject = this.realm.fbLibraries.react; (0, _invariant.default)(reactLibraryObject, "Unable to find React library reference in scope."); return reactLibraryObject; } } exports.ResidualReactElementVisitor = ResidualReactElementVisitor; //# sourceMappingURL=ResidualReactElementVisitor.js.map