prepack
Version:
Execute a JS bundle, serialize global state and side effects to a snapshot that can be quickly restored.
289 lines (247 loc) • 10.7 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = function (ast, strictCode, env, realm) {
(0, _invariant2.default)(realm.react.enabled, "JSXElements can only be evaluated with the reactEnabled option");
let openingElement = ast.openingElement;
let type = evaluateJSXIdentifier(openingElement.name, strictCode, env, realm);
let children = evaluateJSXChildren(ast.children, strictCode, env, realm);
let config = evaluateJSXAttributes(openingElement.attributes, strictCode, env, realm);
(0, _invariant2.default)(type instanceof _index.Value);
return (0, _elements.createReactElement)(realm, type, config, children);
};
var _index = require("../values/index.js");
var _jsx = require("../react/jsx.js");
var _babelTypes = require("babel-types");
var t = _interopRequireWildcard(_babelTypes);
var _index2 = require("../methods/index.js");
var _singletons = require("../singletons.js");
var _invariant = require("../invariant.js");
var _invariant2 = _interopRequireDefault(_invariant);
var _elements = require("../react/elements.js");
var _utils = require("../react/utils.js");
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; } }
// taken from Babel
function cleanJSXElementLiteralChild(child) {
let lines = child.split(/\r\n|\n|\r/);
let lastNonEmptyLine = 0;
for (let i = 0; i < lines.length; i++) {
if (lines[i].match(/[^ \t]/)) {
lastNonEmptyLine = i;
}
}
let str = "";
for (let i = 0; i < lines.length; i++) {
let line = lines[i];
let isFirstLine = i === 0;
let isLastLine = i === lines.length - 1;
let isLastNonEmptyLine = i === lastNonEmptyLine;
// replace rendered whitespace tabs with spaces
let trimmedLine = line.replace(/\t/g, " ");
// trim whitespace touching a newline
if (!isFirstLine) {
trimmedLine = trimmedLine.replace(/^[ ]+/, "");
}
// trim whitespace touching an endline
if (!isLastLine) {
trimmedLine = trimmedLine.replace(/[ ]+$/, "");
}
if (trimmedLine) {
if (!isLastNonEmptyLine) {
trimmedLine += " ";
}
str += trimmedLine;
}
}
if (str) {
return str;
}
return null;
} /**
* 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 evaluateJSXMemberExpression(ast, strictCode, env, realm) {
switch (ast.type) {
case "JSXIdentifier":
return _singletons.Environment.GetValue(realm, _singletons.Environment.ResolveBinding(realm, ast.name, strictCode, env));
case "JSXMemberExpression":
return _singletons.Environment.GetValue(realm, env.evaluate((0, _jsx.convertJSXExpressionToIdentifier)(ast), strictCode));
default:
(0, _invariant2.default)(false, "Unknown JSX Identifier");
}
}
function evaluateJSXIdentifier(ast, strictCode, env, realm) {
if (isTagName(ast)) {
// special cased lower-case and custom elements
return new _index.StringValue(realm, ast.name);
}
return evaluateJSXMemberExpression(ast, strictCode, env, realm);
}
function evaluateJSXValue(value, strictCode, env, realm) {
if (value != null) {
switch (value.type) {
case "JSXText":
return new _index.StringValue(realm, value.value);
case "StringLiteral":
return new _index.StringValue(realm, value.value);
case "JSXExpressionContainer":
return _singletons.Environment.GetValue(realm, env.evaluate(value.expression, strictCode));
case "JSXElement":
return _singletons.Environment.GetValue(realm, env.evaluate(value, strictCode));
default:
(0, _invariant2.default)(false, `Unknown JSX value type: ${value.type}`);
}
}
(0, _invariant2.default)(false, `Null or undefined value passed when trying to evaluate JSX node value`);
}
function isTagName(ast) {
return ast.type === "JSXIdentifier" && /^[a-z]|\-/.test(ast.name);
}
function evaluateJSXChildren(children, strictCode, env, realm) {
if (children.length === 0) {
return realm.intrinsics.undefined;
}
if (children.length === 1) {
let singleChild = evaluateJSXValue(children[0], strictCode, env, realm);
if (singleChild instanceof _index.StringValue) {
let text = cleanJSXElementLiteralChild(singleChild.value);
if (text !== null) {
singleChild.value = text;
}
}
return singleChild;
}
let array = _singletons.Create.ArrayCreate(realm, 0);
let dynamicChildrenLength = children.length;
let dynamicIterator = 0;
let lastChildValue = realm.intrinsics.undefined;
for (let i = 0; i < children.length; i++) {
let value = evaluateJSXValue(children[i], strictCode, env, realm);
if (value instanceof _index.StringValue) {
let text = cleanJSXElementLiteralChild(value.value);
if (text === null) {
dynamicChildrenLength--;
// this is a space full of whitespace, so let's proceed
continue;
} else {
value.value = text;
}
}
lastChildValue = value;
_singletons.Create.CreateDataPropertyOrThrow(realm, array, "" + dynamicIterator, value);
dynamicIterator++;
}
if (dynamicChildrenLength === 1) {
return lastChildValue;
}
_singletons.Properties.Set(realm, array, "length", new _index.NumberValue(realm, dynamicChildrenLength), false);
return array;
}
function isObjectEmpty(object) {
let propertyCount = 0;
for (let [, binding] of object.properties) {
if (binding && binding.descriptor && binding.descriptor.enumerable) {
propertyCount++;
}
}
return propertyCount === 0;
}
function evaluateJSXAttributes(astAttributes, strictCode, env, realm) {
let config = _singletons.Create.ObjectCreate(realm, realm.intrinsics.ObjectPrototype);
// start by having key and ref deleted, if they actually exist, they will be added later
(0, _utils.deleteRefAndKeyFromProps)(realm, config);
let abstractPropsArgs = [];
let containsAbstractSpreadAttribute = false;
let mayContainRefOrKey = false;
let attributesAssigned = 0;
let spreadValue;
const setConfigProperty = (name, value) => {
(0, _invariant2.default)(config instanceof _index.ObjectValue);
if (name === "key" || name === "ref") {
mayContainRefOrKey = true;
}
_singletons.Properties.Set(realm, config, name, value, true);
attributesAssigned++;
};
for (let astAttribute of astAttributes) {
switch (astAttribute.type) {
case "JSXAttribute":
let { name, value } = astAttribute;
(0, _invariant2.default)(name.type === "JSXIdentifier", `JSX attribute name type not supported: ${astAttribute.type}`);
setConfigProperty(name.name, evaluateJSXValue(value, strictCode, env, realm));
break;
case "JSXSpreadAttribute":
spreadValue = _singletons.Environment.GetValue(realm, env.evaluate(astAttribute.argument, strictCode));
if (spreadValue instanceof _index.ObjectValue && !spreadValue.isPartialObject()) {
for (let [spreadPropKey, binding] of spreadValue.properties) {
if (binding && binding.descriptor && binding.descriptor.enumerable) {
setConfigProperty(spreadPropKey, (0, _index2.Get)(realm, spreadValue, spreadPropKey));
}
}
} else {
containsAbstractSpreadAttribute = true;
(0, _invariant2.default)(spreadValue instanceof _index.AbstractValue || spreadValue instanceof _index.ObjectValue);
if (!(0, _utils.objectHasNoPartialKeyAndRef)(realm, spreadValue)) {
mayContainRefOrKey = true;
}
if (!isObjectEmpty(config)) {
abstractPropsArgs.push(config);
}
abstractPropsArgs.push(spreadValue);
config = _singletons.Create.ObjectCreate(realm, realm.intrinsics.ObjectPrototype);
(0, _utils.deleteRefAndKeyFromProps)(realm, config);
}
break;
default:
(0, _invariant2.default)(false, `Unknown JSX attribute type: ${astAttribute.type}`);
}
}
if (containsAbstractSpreadAttribute) {
// if we haven't assigned any attributes and we are dealing with a single
// spread attribute, we can just make the spread object the props
if (attributesAssigned === 0 && (spreadValue instanceof _index.ObjectValue && spreadValue.isPartialObject() || spreadValue instanceof _index.AbstractValue)) {
// the spread is partial, so we can re-use that value
config = spreadValue;
if (config instanceof _index.ObjectValue || config instanceof _index.AbstractObjectValue) {
// as we're applying a spread, the config needs to be simple/partial
config.makePartial();
config.makeSimple();
}
} else {
// we create an abstract Object.assign() to deal with the fact that we don't what
// the props are because they contain abstract spread attributes that we can't
// evaluate ahead of time
// push the current config
if (config.properties.size > 0) {
abstractPropsArgs.push(config);
}
// create a new config object that will be the target of the Object.assign
config = _singletons.Create.ObjectCreate(realm, realm.intrinsics.ObjectPrototype);
// as this is "config that is abstract, we need to make it partial and simple
config.makePartial();
config.makeSimple();
// get the global Object.assign
let globalObj = (0, _index2.Get)(realm, realm.$GlobalObject, "Object");
(0, _invariant2.default)(globalObj instanceof _index.ObjectValue);
let objAssign = (0, _index2.Get)(realm, globalObj, "assign");
(0, _invariant2.default)(realm.generator);
(0, _invariant2.default)(realm.generator);
_index.AbstractValue.createTemporalFromBuildFunction(realm, _index.FunctionValue, [objAssign, config, ...abstractPropsArgs], ([methodNode, ..._args]) => {
return t.callExpression(methodNode, _args);
});
if (!mayContainRefOrKey) {
(0, _utils.deleteRefAndKeyFromProps)(realm, config);
}
}
}
(0, _invariant2.default)(config instanceof _index.ObjectValue || config instanceof _index.AbstractValue);
return config;
}
//# sourceMappingURL=JSXElement.js.map