UNPKG

prepack

Version:

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

235 lines (204 loc) 10.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.getInitialProps = getInitialProps; exports.getInitialContext = getInitialContext; exports.createSimpleClassInstance = createSimpleClassInstance; exports.createClassInstanceForFirstRenderOnly = createClassInstanceForFirstRenderOnly; exports.createClassInstance = createClassInstance; exports.evaluateClassConstructor = evaluateClassConstructor; var _realm = require("../realm.js"); var _index = require("../values/index.js"); var _babelTypes = require("babel-types"); var t = _interopRequireWildcard(_babelTypes); var _utils = require("./utils"); var _errors = require("./errors.js"); var _index2 = require("../methods/index.js"); var _singletons = require("../singletons.js"); var _invariant = require("../invariant.js"); var _invariant2 = _interopRequireDefault(_invariant); 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; } } /** * 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. */ const lifecycleMethods = new Set(["componentWillUnmount", "componentDidMount", "componentWillMount", "componentDidUpdate", "componentWillUpdate", "componentDidCatch", "componentWillReceiveProps"]); const whitelistedProperties = new Set(["props", "context", "refs"]); function getInitialProps(realm, componentType) { let propsName = null; if (componentType !== null) { if ((0, _utils.valueIsClassComponent)(realm, componentType)) { propsName = "this.props"; } else { // otherwise it's a functional component, where the first paramater of the function is "props" (if it exists) if (componentType.$FormalParameters.length > 0) { let firstParam = componentType.$FormalParameters[0]; if (t.isIdentifier(firstParam)) { propsName = firstParam.name; } } } } let value = _index.AbstractValue.createAbstractObject(realm, propsName || "props"); // props objects don't have a key and ref, so we remove them (0, _utils.deleteRefAndKeyFromProps)(realm, value); (0, _invariant2.default)(value instanceof _index.AbstractObjectValue); return value; } function getInitialContext(realm, componentType) { let contextName = null; if ((0, _utils.valueIsClassComponent)(realm, componentType)) { // it's a class component, so we need to check the type on for context of the component prototype let superTypeParameters = componentType.$SuperTypeParameters; contextName = "this.context"; if (superTypeParameters !== undefined) { throw new _errors.ExpectedBailOut("context on class components not yet supported"); } } else { // otherwise it's a functional component, where the second paramater of the function is "context" (if it exists) if (componentType.$FormalParameters.length > 1) { let secondParam = componentType.$FormalParameters[1]; if (t.isIdentifier(secondParam)) { contextName = secondParam.name; } } } let value = _index.AbstractValue.createAbstractObject(realm, contextName || "context"); return value; } function createSimpleClassInstance(realm, componentType, props, context) { let componentPrototype = (0, _index2.Get)(realm, componentType, "prototype"); (0, _invariant2.default)(componentPrototype instanceof _index.ObjectValue); // create an instance object and disable serialization as we don't want to output the internals we set below let instance = new _index.ObjectValue(realm, componentPrototype, "this", true); let allowedPropertyAccess = new Set(["props", "context"]); for (let [name] of componentPrototype.properties) { if (lifecycleMethods.has(name)) { // this error will result in the simple class falling back to a complex class throw new _errors.SimpleClassBailOut("lifecycle methods are not supported on simple classes"); } else if (name !== "constructor") { allowedPropertyAccess.add(name); _singletons.Properties.Set(realm, instance, name, (0, _index2.Get)(realm, componentPrototype, name), true); } } // assign props _singletons.Properties.Set(realm, instance, "props", props, true); // assign context _singletons.Properties.Set(realm, instance, "context", context, true); // as this object is simple, we want to check if any access to anything other than // "this.props" or "this.context" or methods on the class occur let $GetOwnProperty = instance.$GetOwnProperty; instance.$GetOwnProperty = P => { if (!allowedPropertyAccess.has(P)) { // this error will result in the simple class falling back to a complex class throw new _errors.SimpleClassBailOut("access to basic class instance properties is not supported on simple classes"); } return $GetOwnProperty.call(instance, P); }; // enable serialization to support simple instance variables properties instance.refuseSerialization = false; // return the instance return instance; } function deeplyApplyInstancePrototypeProperties(realm, instance, componentPrototype, classMetadata) { let { instanceProperties, instanceSymbols } = classMetadata; let proto = componentPrototype.$Prototype; if (proto instanceof _index.ObjectValue && proto !== realm.intrinsics.ObjectPrototype) { deeplyApplyInstancePrototypeProperties(realm, instance, proto, classMetadata); } for (let [name] of componentPrototype.properties) { // ensure we don't set properties that were defined on the instance if (name !== "constructor" && !instanceProperties.has(name)) { _singletons.Properties.Set(realm, instance, name, (0, _index2.Get)(realm, componentPrototype, name), true); } } for (let [symbol] of componentPrototype.symbols) { // ensure we don't set symbols that were defined on the instance if (!instanceSymbols.has(symbol)) { _singletons.Properties.Set(realm, instance, symbol, (0, _index2.Get)(realm, componentPrototype, symbol), true); } } } function createClassInstanceForFirstRenderOnly(realm, componentType, props, context) { let instance = (0, _index2.Construct)(realm, componentType, [props, context]); (0, _invariant2.default)(instance instanceof _index.ObjectValue); instance.intrinsicName = "this"; instance.refuseSerialization = true; // assign props _singletons.Properties.Set(realm, instance, "props", props, true); // assign context _singletons.Properties.Set(realm, instance, "context", context, true); // assign a mocked setState let setState = new _index.NativeFunctionValue(realm, undefined, `setState`, 1, (_context, [state, callback]) => { let stateToUpdate = state; let currentState = (0, _index2.Get)(realm, instance, "state"); (0, _invariant2.default)(currentState instanceof _index.ObjectValue); if (state instanceof _index.ECMAScriptSourceFunctionValue && state.$Call) { stateToUpdate = state.$Call(instance, [currentState]); } if (stateToUpdate instanceof _index.ObjectValue) { for (let [key, binding] of stateToUpdate.properties) { if (binding && binding.descriptor && binding.descriptor.enumerable) { let value = (0, _utils.getProperty)(realm, stateToUpdate, key); _singletons.Properties.Set(realm, currentState, key, value, true); } } } if (callback instanceof _index.ECMAScriptSourceFunctionValue && callback.$Call) { callback.$Call(instance, []); } return realm.intrinsics.undefined; }); setState.intrinsicName = "this.setState"; _singletons.Properties.Set(realm, instance, "setState", setState, true); instance.refuseSerialization = false; return instance; } function createClassInstance(realm, componentType, props, context, classMetadata) { let componentPrototype = (0, _index2.Get)(realm, componentType, "prototype"); (0, _invariant2.default)(componentPrototype instanceof _index.ObjectValue); // create an instance object and disable serialization as we don't want to output the internals we set below let instance = new _index.ObjectValue(realm, componentPrototype, "this", true); deeplyApplyInstancePrototypeProperties(realm, instance, componentPrototype, classMetadata); // assign refs _singletons.Properties.Set(realm, instance, "refs", _index.AbstractValue.createAbstractObject(realm, "this.refs"), true); // assign props _singletons.Properties.Set(realm, instance, "props", props, true); // assign context _singletons.Properties.Set(realm, instance, "context", context, true); // enable serialization to support simple instance variables properties instance.refuseSerialization = false; // return the instance in an abstract object let value = _index.AbstractValue.createAbstractObject(realm, "this", instance); (0, _invariant2.default)(value instanceof _index.AbstractObjectValue); return value; } function evaluateClassConstructor(realm, constructorFunc, props, context) { let instanceProperties = new Set(); let instanceSymbols = new Set(); realm.evaluatePure(() => realm.evaluateForEffects(() => { let instance = (0, _index2.Construct)(realm, constructorFunc, [props, context]); (0, _invariant2.default)(instance instanceof _index.ObjectValue); for (let [propertyName] of instance.properties) { if (!whitelistedProperties.has(propertyName)) { instanceProperties.add(propertyName); } } for (let [symbol] of instance.symbols) { instanceSymbols.add(symbol); } return instance; }, /*state*/null, `react component constructor: ${constructorFunc.getName()}`)); return { instanceProperties, instanceSymbols }; } //# sourceMappingURL=components.js.map