prepack
Version:
Execute a JS bundle, serialize global state and side effects to a snapshot that can be quickly restored.
170 lines (148 loc) • 6.03 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
var _realm = require("../realm.js");
var _index = require("../values/index.js");
var _invariant = require("../invariant.js");
var _invariant2 = _interopRequireDefault(_invariant);
var _utils = require("./utils");
var _index2 = require("../methods/index.js");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
// ReactElementSet keeps records around of the values
// of ReactElement/JSX nodes so we can return the same immutable values
// where possible, i.e. <div /> === <div />
//
// Rather than uses hashes, this class uses linked Maps to track equality of objects.
// It does this by recursively iterating through objects, by their properties/symbols and using
// each property key as a map, and then from that map, each value as a map. The value
// then links to the subsequent property/symbol in the object. This approach ensures insertion
// is maintained through all objects.
class ReactElementSet {
constructor(realm, equivalenceSet) {
this.realm = realm;
this.equivalenceSet = equivalenceSet;
this.reactElementRoot = new Map();
this.objectRoot = new Map();
this.arrayRoot = new Map();
this.emptyArray = new _index.ArrayValue(realm);
this.emptyObject = new _index.ObjectValue(realm, realm.intrinsics.ObjectPrototype);
}
_createNode() {
return {
map: new Map(),
value: null
};
}
_getKey(key, map, visitedValues) {
if (!map.has(key)) {
map.set(key, new Map());
}
return map.get(key);
}
_getValue(val, map, visitedValues) {
if (val instanceof _index.StringValue || val instanceof _index.NumberValue) {
val = val.value;
} else if (val instanceof _index.AbstractValue) {
val = this.equivalenceSet.add(val);
} else if (val instanceof _index.ArrayValue) {
val = this._getArrayValue(val, visitedValues);
} else if (val instanceof _index.ObjectValue && !(val instanceof _index.FunctionValue)) {
val = this._getObjectValue(val, visitedValues);
}
if (!map.has(val)) {
map.set(val, this._createNode());
}
return map.get(val);
}
// for objects: [key/symbol] -> [key/symbol]... as nodes
_getObjectValue(object, visitedValues) {
if (visitedValues.has(object)) return object;
visitedValues.add(object);
if ((0, _utils.isReactElement)(object)) {
return this.add(object);
}
let currentMap = this.objectRoot;
let result;
for (let [propName] of object.properties) {
currentMap = this._getKey(propName, currentMap, visitedValues);
let prop = (0, _utils.getProperty)(this.realm, object, propName);
result = this._getValue(prop, currentMap, visitedValues);
currentMap = result.map;
}
for (let [symbol] of object.symbols) {
currentMap = this._getKey(symbol, currentMap, visitedValues);
let prop = (0, _utils.getProperty)(this.realm, object, symbol);
result = this._getValue(prop, currentMap, visitedValues);
currentMap = result.map;
}
if (result === undefined) {
return this.emptyObject;
}
if (result.value === null) {
result.value = object;
}
return result.value;
}
// for arrays: [0] -> [1] -> [2]... as nodes
_getArrayValue(array, visitedValues) {
if (visitedValues.has(array)) return array;
visitedValues.add(array);
let lengthValue = (0, _utils.getProperty)(this.realm, array, "length");
(0, _invariant2.default)(lengthValue instanceof _index.NumberValue);
let length = lengthValue.value;
let currentMap = this.arrayRoot;
let result;
for (let i = 0; i < length; i++) {
currentMap = this._getKey(i, currentMap, visitedValues);
let element = (0, _utils.getProperty)(this.realm, array, "" + i);
result = this._getValue(element, currentMap, visitedValues);
currentMap = result.map;
}
if (result === undefined) {
return this.emptyArray;
}
if (result.value === null) {
result.value = array;
}
return result.value;
}
add(reactElement, visitedValues) {
if (!visitedValues) visitedValues = new Set();
let currentMap = this.reactElementRoot;
// type
currentMap = this._getKey("type", currentMap, visitedValues);
let type = (0, _utils.getProperty)(this.realm, reactElement, "type");
let result = this._getValue(type, currentMap, visitedValues);
currentMap = result.map;
// key
currentMap = this._getKey("key", currentMap, visitedValues);
let key = (0, _utils.getProperty)(this.realm, reactElement, "key");
result = this._getValue(key, currentMap, visitedValues);
currentMap = result.map;
// ref
currentMap = this._getKey("ref", currentMap, visitedValues);
let ref = (0, _utils.getProperty)(this.realm, reactElement, "ref");
result = this._getValue(ref, currentMap, visitedValues);
currentMap = result.map;
// props
currentMap = this._getKey("props", currentMap, visitedValues);
let props = (0, _utils.getProperty)(this.realm, reactElement, "props");
result = this._getValue(props, currentMap, visitedValues);
currentMap = result.map;
if (result.value === null) {
result.value = reactElement;
}
(0, _invariant2.default)(result.value instanceof _index.ObjectValue);
return result.value;
}
}
exports.default = ReactElementSet; /**
* 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.
*/
//# sourceMappingURL=ReactElementSet.js.map