mirror of
https://github.com/facebookarchive/prepack.git
synced 2024-09-11 14:46:37 +03:00
Basic JSX/ReactElement support
Summary: Release notes: adds JSX and ReactElement evaluation/serialization support This adds JSX/ReactElement support to the Prepack serializer so `ReactElement` "like" objects can correctly be converted back to JSX syntax. Example: ```jsx 'use strict'; const externalThing = __abstract('function', 'externalThing'); function Test() { return <div>{ externalThing() }</div> } global.test = Test; ``` Becomes: ```jsx 'use strict'; var _$0 = this; var _0 = function () { return <div>{_1()}</div>; }; var _1 = externalThing; _$0.test = _0; ``` I've also added a JSXElement -> ReactElement evaluator. Closes https://github.com/facebook/prepack/pull/1051 Differential Revision: D6053275 Pulled By: trueadm fbshipit-source-id: 7c31545751ceedba55cdd4581bc6de51a98b412a
This commit is contained in:
parent
b0e977136c
commit
cfc6993e9c
@ -52,8 +52,9 @@
|
||||
"prettier-all": "node ./scripts/prettier.js write"
|
||||
},
|
||||
"dependencies": {
|
||||
"babel-core": "^6.8.0",
|
||||
"babel-core": "^6.26.0",
|
||||
"babel-generator": "^6.8.0",
|
||||
"babel-plugin-transform-react-jsx": "^6.24.1",
|
||||
"babel-template": "^6.9.0",
|
||||
"babel-traverse": "^6.9.0",
|
||||
"babel-types": "^6.9.0",
|
||||
|
@ -41,7 +41,7 @@ exec("flow check --profile", function(error, stdout, stderr) {
|
||||
process.exit(1);
|
||||
}
|
||||
console.log("Biggest cycle: " + cycle_len);
|
||||
let MAX_CYCLE_LEN = 58;
|
||||
let MAX_CYCLE_LEN = 59;
|
||||
if (cycle_len > MAX_CYCLE_LEN) {
|
||||
console.log("Error: You increased cycle length from the previous high of " + MAX_CYCLE_LEN);
|
||||
process.exit(1);
|
||||
|
@ -22,8 +22,15 @@ let fs = require("fs");
|
||||
let vm = require("vm");
|
||||
let os = require("os");
|
||||
let minimist = require("minimist");
|
||||
let babel = require("babel-core");
|
||||
const EOL = os.EOL;
|
||||
|
||||
function transformWithBabel(code, plugins) {
|
||||
return babel.transform(code, {
|
||||
plugins,
|
||||
}).code;
|
||||
}
|
||||
|
||||
function search(dir, relative) {
|
||||
let tests = [];
|
||||
|
||||
@ -87,6 +94,7 @@ function runTest(name, code, options, args) {
|
||||
console.log(chalk.inverse(name) + " " + util.inspect(options));
|
||||
let compatibility = code.includes("// jsc") ? "jsc-600-1-4-17" : undefined;
|
||||
let initializeMoreModules = code.includes("// initialize more modules");
|
||||
let compileJSXWithBabel = code.includes("// babel:jsx");
|
||||
let delayUnsupportedRequires = code.includes("// delay unsupported requires");
|
||||
let functionCloneCountMatch = code.match(/\/\/ serialized function clone count: (\d+)/);
|
||||
options = Object.assign({}, options, {
|
||||
@ -104,6 +112,7 @@ function runTest(name, code, options, args) {
|
||||
if (code.includes("// omit invariants")) options.omitInvariants = true;
|
||||
if (code.includes("// additional functions")) options.additionalFunctions = ["additional1", "additional2"];
|
||||
if (code.includes("// exceeds stack limit")) options.maxStackDepth = 10;
|
||||
if (code.includes("// react")) options.reactEnabled = true;
|
||||
if (code.includes("// throws introspection error")) {
|
||||
try {
|
||||
let realmOptions = {
|
||||
@ -208,9 +217,13 @@ function runTest(name, code, options, args) {
|
||||
}
|
||||
let unique = 27277;
|
||||
let oldUniqueSuffix = "";
|
||||
let expectedCode = code;
|
||||
if (compileJSXWithBabel) {
|
||||
expectedCode = transformWithBabel(expectedCode, ["transform-react-jsx"]);
|
||||
}
|
||||
try {
|
||||
try {
|
||||
expected = exec(`${addedCode}\n(function () {${code} // keep newline here as code may end with comment
|
||||
expected = exec(`${addedCode}\n(function () {${expectedCode} // keep newline here as code may end with comment
|
||||
}).call(this);`);
|
||||
} catch (e) {
|
||||
expected = e;
|
||||
@ -230,6 +243,9 @@ function runTest(name, code, options, args) {
|
||||
break;
|
||||
}
|
||||
let newCode = serialized.code;
|
||||
if (compileJSXWithBabel) {
|
||||
newCode = transformWithBabel(newCode, ["transform-react-jsx"]);
|
||||
}
|
||||
codeIterations.push(newCode);
|
||||
if (args.verbose) console.log(newCode);
|
||||
let markersIssue = false;
|
||||
|
377
src/evaluators/JSXElement.js
Normal file
377
src/evaluators/JSXElement.js
Normal file
@ -0,0 +1,377 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* @flow */
|
||||
|
||||
import type { Realm } from "../realm.js";
|
||||
import type { LexicalEnvironment } from "../environment.js";
|
||||
import { FatalError } from "../errors.js";
|
||||
import type {
|
||||
BabelNode,
|
||||
BabelNodeStringLiteral,
|
||||
BabelNodeJSXText,
|
||||
BabelNodeJSXElement,
|
||||
BabelNodeJSXIdentifier,
|
||||
BabelNodeJSXMemberExpression,
|
||||
BabelNodeJSXAttribute,
|
||||
BabelNodeJSXSpreadAttribute,
|
||||
BabelNodeJSXExpressionContainer,
|
||||
} from "babel-types";
|
||||
import { ArrayValue, StringValue, Value, NumberValue, ObjectValue, SymbolValue } from "../values/index.js";
|
||||
import { convertJSXExpressionToIdentifier } from "../utils/jsx";
|
||||
import * as t from "babel-types";
|
||||
import {
|
||||
Get,
|
||||
GetValue,
|
||||
ResolveBinding,
|
||||
ArrayCreate,
|
||||
CreateDataPropertyOrThrow,
|
||||
ObjectCreate,
|
||||
Set,
|
||||
} from "../methods/index.js";
|
||||
import invariant from "../invariant.js";
|
||||
import { computeBinary } from "./BinaryExpression.js";
|
||||
|
||||
let RESERVED_PROPS = {
|
||||
key: true,
|
||||
ref: true,
|
||||
__self: true,
|
||||
__source: true,
|
||||
};
|
||||
|
||||
let reactElementSymbolKey = "react.element";
|
||||
|
||||
// taken from Babel
|
||||
function cleanJSXElementLiteralChild(child: string): null | string {
|
||||
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;
|
||||
}
|
||||
|
||||
function evaluateJSXMemberExpression(
|
||||
ast: BabelNode,
|
||||
strictCode: boolean,
|
||||
env: LexicalEnvironment,
|
||||
realm: Realm
|
||||
): Value {
|
||||
switch (ast.type) {
|
||||
case "JSXIdentifier":
|
||||
return GetValue(realm, ResolveBinding(realm, ((ast: any): BabelNodeJSXIdentifier).name, strictCode, env));
|
||||
case "JSXMemberExpression":
|
||||
return GetValue(
|
||||
realm,
|
||||
env.evaluate(convertJSXExpressionToIdentifier(((ast: any): BabelNodeJSXMemberExpression)), strictCode)
|
||||
);
|
||||
default:
|
||||
invariant(false, "Unknown JSX Identifier");
|
||||
}
|
||||
}
|
||||
|
||||
function evaluateJSXIdentifier(ast, strictCode, env, realm): Value {
|
||||
if (isTagName(ast)) {
|
||||
// special cased lower-case and custom elements
|
||||
return new StringValue(realm, ((ast: any): BabelNodeJSXIdentifier).name);
|
||||
}
|
||||
return evaluateJSXMemberExpression(ast, strictCode, env, realm);
|
||||
}
|
||||
|
||||
function evaluateJSXValue(value: BabelNode, strictCode: boolean, env: LexicalEnvironment, realm: Realm): Value {
|
||||
if (value != null) {
|
||||
switch (value.type) {
|
||||
case "JSXText":
|
||||
return new StringValue(realm, ((value: any): BabelNodeJSXText).value);
|
||||
case "StringLiteral":
|
||||
return new StringValue(realm, ((value: any): BabelNodeStringLiteral).value);
|
||||
case "JSXExpressionContainer":
|
||||
return GetValue(realm, env.evaluate(((value: any): BabelNodeJSXExpressionContainer).expression, strictCode));
|
||||
case "JSXElement":
|
||||
return GetValue(realm, env.evaluate(value, strictCode));
|
||||
default:
|
||||
invariant(false, `Unknown JSX value type: ${value.type}`);
|
||||
}
|
||||
}
|
||||
invariant(false, `Null or undefined value passed when trying to evaluate JSX node value`);
|
||||
}
|
||||
|
||||
function isTagName(ast: BabelNode): boolean {
|
||||
return ast.type === "JSXIdentifier" && /^[a-z]|\-/.test(((ast: any): BabelNodeJSXIdentifier).name);
|
||||
}
|
||||
|
||||
function getDefaultProps(
|
||||
elementType: BabelNodeJSXIdentifier | BabelNodeJSXMemberExpression,
|
||||
env,
|
||||
realm: Realm
|
||||
): null | ObjectValue {
|
||||
let name;
|
||||
if (elementType.type === "JSXIdentifier") {
|
||||
name = elementType.name;
|
||||
}
|
||||
if (!isTagName(elementType) && typeof name === "string") {
|
||||
// find the value of "ComponentXXX.defaultProps"
|
||||
let defaultProps = GetValue(
|
||||
realm,
|
||||
env.evaluate(t.memberExpression(t.identifier(name), t.identifier("defaultProps")), false)
|
||||
);
|
||||
|
||||
if (defaultProps instanceof ObjectValue) {
|
||||
return defaultProps;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function evaluateJSXChildren(
|
||||
children: Array<BabelNode>,
|
||||
strictCode: boolean,
|
||||
env: LexicalEnvironment,
|
||||
realm: Realm
|
||||
): ArrayValue | Value | null {
|
||||
if (children.length === 0) {
|
||||
return null;
|
||||
}
|
||||
if (children.length === 1) {
|
||||
let singleChild = evaluateJSXValue(children[0], strictCode, env, realm);
|
||||
|
||||
if (singleChild instanceof StringValue) {
|
||||
let text = cleanJSXElementLiteralChild(singleChild.value);
|
||||
if (text !== null) {
|
||||
singleChild.value = text;
|
||||
}
|
||||
}
|
||||
return singleChild;
|
||||
}
|
||||
let array = ArrayCreate(realm, 0);
|
||||
let dynamicChildrenLength = children.length;
|
||||
let dynamicIterator = 0;
|
||||
let lastChildValue = null;
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
let value = evaluateJSXValue(children[i], strictCode, env, realm);
|
||||
if (value instanceof 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;
|
||||
CreateDataPropertyOrThrow(realm, array, "" + dynamicIterator, value);
|
||||
dynamicIterator++;
|
||||
}
|
||||
if (dynamicChildrenLength === 1) {
|
||||
return lastChildValue;
|
||||
}
|
||||
|
||||
Set(realm, array, "length", new NumberValue(realm, dynamicChildrenLength), false);
|
||||
return array;
|
||||
}
|
||||
|
||||
function evaluateJSXAttributes(
|
||||
elementType: BabelNodeJSXIdentifier | BabelNodeJSXMemberExpression,
|
||||
astAttributes: Array<BabelNodeJSXAttribute | BabelNodeJSXSpreadAttribute>,
|
||||
astChildren: Array<BabelNode>,
|
||||
strictCode: boolean,
|
||||
env: LexicalEnvironment,
|
||||
realm: Realm
|
||||
): { attributes: Map<string, Value>, children: ArrayValue | Value | null } {
|
||||
let attributes = new Map();
|
||||
let children = evaluateJSXChildren(astChildren, strictCode, env, realm);
|
||||
let defaultProps = getDefaultProps(elementType, env, realm);
|
||||
|
||||
// defaultProps are a bit like default function arguments
|
||||
// if an actual value exists, it should overwrite the default value
|
||||
if (defaultProps !== null) {
|
||||
for (let [key] of defaultProps.properties) {
|
||||
let defaultPropValue = Get(realm, defaultProps, key);
|
||||
|
||||
if (defaultPropValue instanceof Value) {
|
||||
if (key === "children") {
|
||||
if (children === null) {
|
||||
children = defaultPropValue;
|
||||
}
|
||||
} else {
|
||||
attributes.set(key, defaultPropValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (let astAttribute of astAttributes) {
|
||||
switch (astAttribute.type) {
|
||||
case "JSXAttribute":
|
||||
let { name, value } = astAttribute;
|
||||
|
||||
invariant(name.type === "JSXIdentifier", `JSX attribute name type not supported: ${astAttribute.type}`);
|
||||
attributes.set(name.name, evaluateJSXValue(((value: any): BabelNodeJSXIdentifier), strictCode, env, realm));
|
||||
break;
|
||||
case "JSXSpreadAttribute":
|
||||
let spreadValue = GetValue(realm, env.evaluate(astAttribute.argument, strictCode));
|
||||
|
||||
if (spreadValue instanceof ObjectValue) {
|
||||
for (let [key, spreadProp] of spreadValue.properties) {
|
||||
if (spreadProp !== undefined && spreadProp.descriptor !== undefined) {
|
||||
let spreadPropValue = spreadProp.descriptor.value;
|
||||
|
||||
if (spreadPropValue instanceof Value) {
|
||||
if (key === "children") {
|
||||
children = spreadPropValue;
|
||||
} else {
|
||||
attributes.set(key, spreadPropValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw new FatalError("ObjectValues are the only supported value for JSX Spread Attributes");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
invariant(false, `Unknown JSX attribute type:: ${astAttribute.type}`);
|
||||
}
|
||||
}
|
||||
return {
|
||||
attributes,
|
||||
children,
|
||||
};
|
||||
}
|
||||
|
||||
function getReactElementSymbol(realm: Realm): SymbolValue {
|
||||
let reactElementSymbol = realm.react.reactElementSymbol;
|
||||
if (reactElementSymbol !== undefined) {
|
||||
return reactElementSymbol;
|
||||
}
|
||||
let SymbolFor = realm.intrinsics.Symbol.properties.get("for");
|
||||
if (SymbolFor !== undefined) {
|
||||
let SymbolForDescriptor = SymbolFor.descriptor;
|
||||
|
||||
if (SymbolForDescriptor !== undefined) {
|
||||
let SymbolForValue = SymbolForDescriptor.value;
|
||||
if (SymbolForValue !== undefined && typeof SymbolForValue.$Call === "function") {
|
||||
realm.react.reactElementSymbol = reactElementSymbol = SymbolForValue.$Call(realm.intrinsics.Symbol, [
|
||||
new StringValue(realm, reactElementSymbolKey),
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
invariant(reactElementSymbol instanceof SymbolValue, `ReactElement "$$typeof" property was not a symbol`);
|
||||
return reactElementSymbol;
|
||||
}
|
||||
|
||||
function createReactProps(
|
||||
realm: Realm,
|
||||
type: Value,
|
||||
attributes: Map<string, Value>,
|
||||
children,
|
||||
env: LexicalEnvironment
|
||||
): ObjectValue {
|
||||
let obj = ObjectCreate(realm, realm.intrinsics.ObjectPrototype);
|
||||
for (let [key, value] of attributes) {
|
||||
if (typeof key === "string") {
|
||||
if (RESERVED_PROPS.hasOwnProperty(key)) {
|
||||
continue;
|
||||
}
|
||||
CreateDataPropertyOrThrow(realm, obj, key, value);
|
||||
}
|
||||
}
|
||||
if (children !== null) {
|
||||
CreateDataPropertyOrThrow(realm, obj, "children", children);
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
function createReactElement(realm: Realm, type: Value, key: Value, ref: Value, props: ObjectValue): ObjectValue {
|
||||
let obj = ObjectCreate(realm, realm.intrinsics.ObjectPrototype);
|
||||
CreateDataPropertyOrThrow(realm, obj, "$$typeof", getReactElementSymbol(realm));
|
||||
CreateDataPropertyOrThrow(realm, obj, "type", type);
|
||||
CreateDataPropertyOrThrow(realm, obj, "key", key);
|
||||
CreateDataPropertyOrThrow(realm, obj, "ref", ref);
|
||||
CreateDataPropertyOrThrow(realm, obj, "props", props);
|
||||
CreateDataPropertyOrThrow(realm, obj, "_owner", realm.intrinsics.null);
|
||||
return obj;
|
||||
}
|
||||
|
||||
export default function(
|
||||
ast: BabelNodeJSXElement,
|
||||
strictCode: boolean,
|
||||
env: LexicalEnvironment,
|
||||
realm: Realm
|
||||
): ObjectValue {
|
||||
invariant(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 { attributes, children } = evaluateJSXAttributes(
|
||||
openingElement.name,
|
||||
openingElement.attributes,
|
||||
ast.children,
|
||||
strictCode,
|
||||
env,
|
||||
realm
|
||||
);
|
||||
let key = attributes.get("key") || realm.intrinsics.null;
|
||||
let ref = attributes.get("ref") || realm.intrinsics.null;
|
||||
|
||||
if (key === realm.intrinsics.undefined) {
|
||||
key = realm.intrinsics.null;
|
||||
}
|
||||
if (ref === realm.intrinsics.undefined) {
|
||||
ref = realm.intrinsics.null;
|
||||
}
|
||||
|
||||
// React uses keys to identify nodes as they get updated through the reconcilation
|
||||
// phase. Keys are used in a map and thus need to be converted to strings
|
||||
if (key !== realm.intrinsics.null) {
|
||||
key = computeBinary(realm, "+", realm.intrinsics.emptyString, key);
|
||||
}
|
||||
let props = createReactProps(realm, type, attributes, children, env);
|
||||
|
||||
return createReactElement(realm, type, key, ref, props);
|
||||
}
|
@ -62,3 +62,4 @@ export { default as VariableDeclaration } from "./VariableDeclaration.js";
|
||||
export { default as WhileStatement } from "./WhileStatement.js";
|
||||
export { default as WithStatement } from "./WithStatement.js";
|
||||
export { default as YieldExpression } from "./YieldExpression.js";
|
||||
export { default as JSXElement } from "./JSXElement.js";
|
||||
|
@ -26,6 +26,7 @@ export type RealmOptions = {
|
||||
strictlyMonotonicDateNow?: boolean,
|
||||
timeout?: number,
|
||||
maxStackDepth?: number,
|
||||
reactEnabled?: boolean,
|
||||
};
|
||||
|
||||
export type SerializerOptions = {
|
||||
|
@ -84,6 +84,7 @@ function run(
|
||||
serialize: false,
|
||||
residual: false,
|
||||
profile: false,
|
||||
reactEnabled: false,
|
||||
};
|
||||
|
||||
while (args.length) {
|
||||
|
@ -27,6 +27,7 @@ export type PrepackOptions = {|
|
||||
omitInvariants?: boolean,
|
||||
outputFilename?: string,
|
||||
profile?: boolean,
|
||||
reactEnabled?: boolean,
|
||||
residual?: boolean,
|
||||
serialize?: boolean,
|
||||
inlineExpressions?: boolean,
|
||||
@ -47,6 +48,7 @@ export function getRealmOptions({
|
||||
mathRandomSeed,
|
||||
omitInvariants = false,
|
||||
uniqueSuffix,
|
||||
reactEnabled,
|
||||
residual,
|
||||
serialize = !residual,
|
||||
strictlyMonotonicDateNow,
|
||||
@ -60,6 +62,7 @@ export function getRealmOptions({
|
||||
mathRandomSeed,
|
||||
omitInvariants,
|
||||
uniqueSuffix,
|
||||
reactEnabled,
|
||||
residual,
|
||||
serialize,
|
||||
strictlyMonotonicDateNow,
|
||||
|
10
src/realm.js
10
src/realm.js
@ -174,6 +174,11 @@ export class Realm {
|
||||
this.partialEvaluators = (Object.create(null): any);
|
||||
this.$GlobalEnv = ((undefined: any): LexicalEnvironment);
|
||||
|
||||
this.react = {
|
||||
enabled: opts.reactEnabled || false,
|
||||
reactElementSymbol: undefined,
|
||||
};
|
||||
|
||||
this.errorHandler = opts.errorHandler;
|
||||
|
||||
this.globalSymbolRegistry = [];
|
||||
@ -205,6 +210,11 @@ export class Realm {
|
||||
$GlobalEnv: LexicalEnvironment;
|
||||
intrinsics: Intrinsics;
|
||||
|
||||
react: {
|
||||
enabled: boolean,
|
||||
reactElementSymbol?: SymbolValue,
|
||||
};
|
||||
|
||||
$GlobalObject: ObjectValue | AbstractObjectValue;
|
||||
compatibility: Compatibility;
|
||||
|
||||
|
@ -45,7 +45,9 @@ function serialize(realm: Realm, res: Value | AbruptCompletion): any {
|
||||
return res;
|
||||
}
|
||||
|
||||
let realm = construct_realm();
|
||||
let realm = construct_realm({
|
||||
reactEnabled: true,
|
||||
});
|
||||
initializeGlobals(realm);
|
||||
|
||||
repl.start({
|
||||
|
@ -11,8 +11,9 @@
|
||||
|
||||
import { Realm } from "../realm.js";
|
||||
import type { Descriptor, PropertyBinding } from "../types.js";
|
||||
import { ToLength, IsArray, Get } from "../methods/index.js";
|
||||
import { ToLength, IsArray, Get, IsAccessorDescriptor } from "../methods/index.js";
|
||||
import {
|
||||
ArrayValue,
|
||||
BoundFunctionValue,
|
||||
ProxyValue,
|
||||
SymbolValue,
|
||||
@ -28,8 +29,15 @@ import {
|
||||
NativeFunctionValue,
|
||||
UndefinedValue,
|
||||
} from "../values/index.js";
|
||||
import {
|
||||
convertExpressionToJSXIdentifier,
|
||||
convertKeyValueToJSXAttribute,
|
||||
applyKeysToNestedArray,
|
||||
isReactElement,
|
||||
} from "../utils/jsx";
|
||||
import * as t from "babel-types";
|
||||
import type {
|
||||
BabelNodeArrayExpression,
|
||||
BabelNodeExpression,
|
||||
BabelNodeStatement,
|
||||
BabelNodeIdentifier,
|
||||
@ -42,7 +50,13 @@ import type {
|
||||
import { Generator, PreludeGenerator, NameGenerator } from "../utils/generator.js";
|
||||
import type { SerializationContext } from "../utils/generator.js";
|
||||
import invariant from "../invariant.js";
|
||||
import type { ResidualFunctionBinding, FunctionInfo, FunctionInstance, AdditionalFunctionInfo } from "./types.js";
|
||||
import type {
|
||||
ResidualFunctionBinding,
|
||||
FunctionInfo,
|
||||
FunctionInstance,
|
||||
AdditionalFunctionInfo,
|
||||
ReactSerializerState,
|
||||
} from "./types.js";
|
||||
import { TimingStatistics, SerializerStatistics } from "./types.js";
|
||||
import { Logger } from "./logger.js";
|
||||
import { Modules } from "./modules.js";
|
||||
@ -70,13 +84,15 @@ export class ResidualHeapSerializer {
|
||||
referencedDeclaredValues: Set<AbstractValue>,
|
||||
additionalFunctionValuesAndEffects: Map<FunctionValue, Effects> | void,
|
||||
additionalFunctionValueInfos: Map<FunctionValue, AdditionalFunctionInfo>,
|
||||
statistics: SerializerStatistics
|
||||
statistics: SerializerStatistics,
|
||||
react: ReactSerializerState
|
||||
) {
|
||||
this.realm = realm;
|
||||
this.logger = logger;
|
||||
this.modules = modules;
|
||||
this.residualHeapValueIdentifiers = residualHeapValueIdentifiers;
|
||||
this.statistics = statistics;
|
||||
this.react = react;
|
||||
|
||||
let realmGenerator = this.realm.generator;
|
||||
invariant(realmGenerator);
|
||||
@ -166,6 +182,7 @@ export class ResidualHeapSerializer {
|
||||
referencedDeclaredValues: Set<AbstractValue>;
|
||||
activeGeneratorBodies: Map<Generator, Array<BabelNodeStatement>>;
|
||||
additionalFunctionValuesAndEffects: Map<FunctionValue, Effects> | void;
|
||||
react: ReactSerializerState;
|
||||
// function values nested in additional functions can't delay initializations
|
||||
// TODO: revisit this and fix additional functions to be capable of delaying initializations
|
||||
additionalFunctionValueNestedFunctions: Set<FunctionValue>;
|
||||
@ -705,6 +722,119 @@ export class ResidualHeapSerializer {
|
||||
return t.arrayExpression(initProperties);
|
||||
}
|
||||
|
||||
_getPropertyValue(properties: Map<string, any>, key: string) {
|
||||
if (properties.has(key)) {
|
||||
let val = properties.get(key);
|
||||
|
||||
if (val !== undefined) {
|
||||
let descriptor = val.descriptor;
|
||||
invariant(!IsAccessorDescriptor(this.realm, descriptor), "expected descriptor to be a non-accessor property");
|
||||
|
||||
if (descriptor !== undefined) {
|
||||
let descriptorValue = descriptor.value;
|
||||
|
||||
if (descriptorValue !== undefined) {
|
||||
return descriptorValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
_serializeValueReactElementChild(child: Value): BabelNode {
|
||||
if (isReactElement(child)) {
|
||||
// if we know it's a ReactElement, we add the value to the serializedValues
|
||||
// and short cut to get back the JSX expression so we don't emit additional data
|
||||
// we do this to ensure child JSXElements can get keys assigned if needed
|
||||
this.serializedValues.add(child);
|
||||
return this._serializeValueObject(((child: any): ObjectValue));
|
||||
}
|
||||
const expr = this.serializeValue(child);
|
||||
|
||||
if (t.isArrayExpression(expr)) {
|
||||
applyKeysToNestedArray(((expr: any): BabelNodeArrayExpression), true, this.react.usedReactElementKeys);
|
||||
} else if (t.isStringLiteral(expr) || t.isNumericLiteral(expr)) {
|
||||
return t.jSXText(((expr: any).value: string) + "");
|
||||
} else if (t.isJSXElement(expr)) {
|
||||
return expr;
|
||||
}
|
||||
return t.jSXExpressionContainer(expr);
|
||||
}
|
||||
|
||||
_serializeValueReactElement(val: ObjectValue): BabelNodeExpression {
|
||||
let objectProperties: Map<string, any> = val.properties;
|
||||
let typeValue = this._getPropertyValue(objectProperties, "type");
|
||||
let keyValue = this._getPropertyValue(objectProperties, "key");
|
||||
let refValue = this._getPropertyValue(objectProperties, "ref");
|
||||
let propsValue = this._getPropertyValue(objectProperties, "props");
|
||||
|
||||
invariant(typeValue !== null, "JSXElement type of null");
|
||||
|
||||
let identifier = convertExpressionToJSXIdentifier(this.serializeValue(typeValue), true);
|
||||
let attributes = [];
|
||||
let children = [];
|
||||
|
||||
if (keyValue !== null) {
|
||||
let keyExpr = this.serializeValue(keyValue);
|
||||
if (keyExpr.type !== "NullLiteral") {
|
||||
attributes.push(convertKeyValueToJSXAttribute("key", keyExpr));
|
||||
}
|
||||
}
|
||||
|
||||
if (refValue !== null) {
|
||||
let refExpr = this.serializeValue(refValue);
|
||||
if (refExpr.type !== "NullLiteral") {
|
||||
attributes.push(convertKeyValueToJSXAttribute("ref", refExpr));
|
||||
}
|
||||
}
|
||||
|
||||
if (propsValue instanceof ObjectValue) {
|
||||
// the propsValue is visited to get the properties, but we don't emit it as the object
|
||||
// is contained within a JSXOpeningElement
|
||||
this.serializedValues.add(propsValue);
|
||||
// have to case propsValue to ObjectValue or Flow complains that propsValues can be null/undefined
|
||||
for (let [key, propertyBinding] of (propsValue: ObjectValue).properties) {
|
||||
let desc = propertyBinding.descriptor;
|
||||
if (desc === undefined) continue; // deleted
|
||||
invariant(!IsAccessorDescriptor(this.realm, desc), "expected descriptor to be a non-accessor property");
|
||||
|
||||
invariant(key !== "key" && key !== "ref", `"${key}" is a reserved prop name`);
|
||||
|
||||
if (key === "children" && desc.value !== undefined) {
|
||||
let childrenValue = desc.value;
|
||||
if (childrenValue instanceof ArrayValue) {
|
||||
this.serializedValues.add(childrenValue);
|
||||
let childrenLength = this._getPropertyValue(childrenValue.properties, "length");
|
||||
let childrenLengthValue = 0;
|
||||
if (childrenLength instanceof NumberValue) {
|
||||
childrenLengthValue = childrenLength.value;
|
||||
for (let i = 0; i < childrenLengthValue; i++) {
|
||||
let child = this._getPropertyValue(childrenValue.properties, "" + i);
|
||||
if (child instanceof Value) {
|
||||
children.push(this._serializeValueReactElementChild(child));
|
||||
} else {
|
||||
this.logger.logError(val, `JSXElement "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._serializeValueReactElementChild(((childrenValue: any): Value)));
|
||||
continue;
|
||||
}
|
||||
if (desc.value instanceof Value) {
|
||||
attributes.push(convertKeyValueToJSXAttribute(key, this.serializeValue(desc.value)));
|
||||
}
|
||||
}
|
||||
}
|
||||
let openingElement = t.jSXOpeningElement(identifier, (attributes: any), children.length === 0);
|
||||
let closingElement = t.jSXClosingElement(identifier);
|
||||
|
||||
return t.jSXElement(openingElement, closingElement, children, children.length === 0);
|
||||
}
|
||||
|
||||
_serializeValueMap(val: ObjectValue): BabelNodeExpression {
|
||||
let kind = val.getKind();
|
||||
let elems = [];
|
||||
@ -988,6 +1118,8 @@ export class ResidualHeapSerializer {
|
||||
return this._serializeValueTypedArrayOrDataView(val);
|
||||
case "ArrayBuffer":
|
||||
return this._serializeValueArrayBuffer(val);
|
||||
case "ReactElement":
|
||||
return this._serializeValueReactElement(val);
|
||||
case "Map":
|
||||
case "WeakMap":
|
||||
return this._serializeValueMap(val);
|
||||
|
@ -13,7 +13,7 @@ import { GlobalEnvironmentRecord, DeclarativeEnvironmentRecord } from "../enviro
|
||||
import { FatalError } from "../errors.js";
|
||||
import { Realm } from "../realm.js";
|
||||
import type { Effects } from "../realm.js";
|
||||
import type { Descriptor, PropertyBinding } from "../types.js";
|
||||
import type { Descriptor, PropertyBinding, ObjectKind } from "../types.js";
|
||||
import { IsUnresolvableReference, ToLength, ResolveBinding, HashSet, IsArray, Get } from "../methods/index.js";
|
||||
import {
|
||||
BoundFunctionValue,
|
||||
@ -115,20 +115,28 @@ export class ResidualHeapVisitor {
|
||||
}
|
||||
}
|
||||
|
||||
visitObjectProperties(obj: ObjectValue): void {
|
||||
visitObjectProperties(obj: ObjectValue, kind?: ObjectKind): void {
|
||||
// visit properties
|
||||
for (let [symbol, propertyBinding] of obj.symbols) {
|
||||
invariant(propertyBinding);
|
||||
let desc = propertyBinding.descriptor;
|
||||
if (desc === undefined) continue; //deleted
|
||||
this.visitDescriptor(desc);
|
||||
this.visitValue(symbol);
|
||||
if (kind !== "ReactElement") {
|
||||
for (let [symbol, propertyBinding] of obj.symbols) {
|
||||
invariant(propertyBinding);
|
||||
let desc = propertyBinding.descriptor;
|
||||
if (desc === undefined) continue; //deleted
|
||||
this.visitDescriptor(desc);
|
||||
this.visitValue(symbol);
|
||||
}
|
||||
}
|
||||
|
||||
// visit properties
|
||||
for (let propertyBinding of obj.properties.values()) {
|
||||
invariant(propertyBinding);
|
||||
this.visitObjectProperty(propertyBinding);
|
||||
for (let [propertyBindingKey, propertyBindingValue] of obj.properties) {
|
||||
// we don't want to the $$typeof or _owner properties
|
||||
// as this is contained within the JSXElement, otherwise
|
||||
// they we be need to be emitted during serialization
|
||||
if (kind === "ReactElement" && (propertyBindingKey === "$$typeof" || propertyBindingKey === "_owner")) {
|
||||
continue;
|
||||
}
|
||||
invariant(propertyBindingValue);
|
||||
this.visitObjectProperty(propertyBindingValue);
|
||||
}
|
||||
|
||||
// inject properties with computed names
|
||||
@ -142,7 +150,12 @@ export class ResidualHeapVisitor {
|
||||
}
|
||||
|
||||
// prototype
|
||||
this.visitObjectPrototype(obj);
|
||||
if (kind !== "ReactElement") {
|
||||
// we don't want to the ReactElement prototype visited
|
||||
// as this is contained within the JSXElement, otherwise
|
||||
// they we be need to be emitted during serialization
|
||||
this.visitObjectPrototype(obj);
|
||||
}
|
||||
if (obj instanceof FunctionValue) this.visitConstructorPrototype(obj);
|
||||
}
|
||||
|
||||
@ -372,7 +385,8 @@ export class ResidualHeapVisitor {
|
||||
}
|
||||
|
||||
visitValueObject(val: ObjectValue): void {
|
||||
this.visitObjectProperties(val);
|
||||
let kind = val.getKind();
|
||||
this.visitObjectProperties(val, kind);
|
||||
|
||||
// If this object is a prototype object that was implicitly created by the runtime
|
||||
// for a constructor, then we can obtain a reference to this object
|
||||
@ -383,12 +397,12 @@ export class ResidualHeapVisitor {
|
||||
return;
|
||||
}
|
||||
|
||||
let kind = val.getKind();
|
||||
switch (kind) {
|
||||
case "RegExp":
|
||||
case "Number":
|
||||
case "String":
|
||||
case "Boolean":
|
||||
case "ReactElement":
|
||||
case "ArrayBuffer":
|
||||
return;
|
||||
case "Date":
|
||||
|
@ -20,6 +20,7 @@ import traverseFast from "../utils/traverse-fast.js";
|
||||
import invariant from "../invariant.js";
|
||||
import type { SerializerOptions } from "../options.js";
|
||||
import { TimingStatistics, SerializerStatistics } from "./types.js";
|
||||
import type { ReactSerializerState } from "./types.js";
|
||||
import { Functions } from "./functions.js";
|
||||
import { Logger } from "./logger.js";
|
||||
import { Modules } from "./modules.js";
|
||||
@ -49,6 +50,9 @@ export class Serializer {
|
||||
if (serializerOptions.trace) this.realm.tracers.push(new LoggingTracer(this.realm));
|
||||
|
||||
this.options = serializerOptions;
|
||||
this.react = {
|
||||
usedReactElementKeys: new Set(),
|
||||
};
|
||||
}
|
||||
|
||||
realm: Realm;
|
||||
@ -57,6 +61,7 @@ export class Serializer {
|
||||
modules: Modules;
|
||||
options: SerializerOptions;
|
||||
statistics: SerializerStatistics;
|
||||
react: ReactSerializerState;
|
||||
|
||||
_execute(sources: Array<SourceFile>, sourceMaps?: boolean = false) {
|
||||
let realm = this.realm;
|
||||
@ -149,7 +154,8 @@ export class Serializer {
|
||||
residualHeapVisitor.referencedDeclaredValues,
|
||||
additionalFunctionValuesAndEffects,
|
||||
residualHeapVisitor.additionalFunctionValueInfos,
|
||||
this.statistics
|
||||
this.statistics,
|
||||
this.react
|
||||
).serialize();
|
||||
if (this.logger.hasErrors()) return undefined;
|
||||
if (timingStats !== undefined) timingStats.referenceCountsTime = Date.now() - timingStats.referenceCountsTime;
|
||||
@ -171,7 +177,8 @@ export class Serializer {
|
||||
residualHeapVisitor.referencedDeclaredValues,
|
||||
additionalFunctionValuesAndEffects,
|
||||
residualHeapVisitor.additionalFunctionValueInfos,
|
||||
this.statistics
|
||||
this.statistics,
|
||||
this.react
|
||||
);
|
||||
|
||||
let ast = residualHeapSerializer.serialize();
|
||||
|
@ -152,3 +152,7 @@ export type LocationService = {
|
||||
getLocation: Value => void | BabelNodeIdentifier,
|
||||
createLocation: () => BabelNodeIdentifier,
|
||||
};
|
||||
|
||||
export type ReactSerializerState = {
|
||||
usedReactElementKeys: Set<string>,
|
||||
};
|
||||
|
@ -14,6 +14,7 @@ import { FunctionValue } from "../values/index.js";
|
||||
import * as t from "babel-types";
|
||||
import type { BabelNodeExpression, BabelNodeCallExpression } from "babel-types";
|
||||
import { BabelTraversePath } from "babel-traverse";
|
||||
import { convertExpressionToJSXIdentifier } from "../utils/jsx";
|
||||
import type { TryQuery, FunctionInfo, ResidualFunctionBinding } from "./types.js";
|
||||
|
||||
export type ClosureRefVisitorState = {
|
||||
@ -52,7 +53,13 @@ function replaceName(path, residualFunctionBinding, name, data) {
|
||||
|
||||
if (residualFunctionBinding && shouldVisit(path.node, data)) {
|
||||
markVisited(residualFunctionBinding.serializedValue, data);
|
||||
path.replaceWith(residualFunctionBinding.serializedValue);
|
||||
let serializedValue = residualFunctionBinding.serializedValue;
|
||||
|
||||
if (path.node.type === "JSXIdentifier" || path.node.type === "JSXMemberIdentifier") {
|
||||
path.replaceWith(convertExpressionToJSXIdentifier((serializedValue: any), true));
|
||||
} else {
|
||||
path.replaceWith(serializedValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -275,5 +275,6 @@ export type ObjectKind =
|
||||
| "ArrayBuffer"
|
||||
| "WeakMap"
|
||||
| "WeakSet"
|
||||
| TypedArrayKind;
|
||||
| TypedArrayKind
|
||||
| "ReactElement";
|
||||
// TODO #26 #712: Promises. All kinds of iterators. Generators.
|
||||
|
161
src/utils/jsx.js
Normal file
161
src/utils/jsx.js
Normal file
@ -0,0 +1,161 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* @flow */
|
||||
|
||||
import * as t from "babel-types";
|
||||
import type {
|
||||
BabelNodeExpression,
|
||||
BabelNodeArrayExpression,
|
||||
BabelNodeJSXElement,
|
||||
BabelNodeJSXMemberExpression,
|
||||
BabelNodeJSXIdentifier,
|
||||
BabelNodeIdentifier,
|
||||
BabelNodeMemberExpression,
|
||||
} from "babel-types";
|
||||
import invariant from "../invariant.js";
|
||||
import { Value, ObjectValue, SymbolValue } from "../values/index.js";
|
||||
import { Get } from "../methods/index.js";
|
||||
|
||||
export function isReactElement(val: Value): boolean {
|
||||
if (val instanceof ObjectValue && val.properties.has("$$typeof")) {
|
||||
let realm = val.$Realm;
|
||||
let $$typeof = Get(realm, val, "$$typeof");
|
||||
if ($$typeof instanceof SymbolValue) {
|
||||
let symbolFromRegistry = realm.globalSymbolRegistry.find(e => e.$Symbol === $$typeof);
|
||||
return symbolFromRegistry !== undefined && symbolFromRegistry.$Key === "react.element";
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export function convertExpressionToJSXIdentifier(
|
||||
expr: BabelNodeExpression,
|
||||
isRoot: boolean
|
||||
): BabelNodeJSXMemberExpression | BabelNodeJSXIdentifier {
|
||||
switch (expr.type) {
|
||||
case "ThisExpression":
|
||||
invariant(isRoot === false, `invalid conversion of root expression to JSXIdentifier for ThisExpression`);
|
||||
return t.jSXIdentifier("this");
|
||||
case "Identifier":
|
||||
let name = expr.name;
|
||||
invariant(
|
||||
// ensure the 1st character of the string is uppercase
|
||||
// for a component unless it is not the root
|
||||
isRoot === false || (name.length > 0 && name[0] === name[0].toUpperCase()),
|
||||
"invalid JSXIdentifer from Identifier, Identifier name must be uppercase"
|
||||
);
|
||||
return t.jSXIdentifier(name);
|
||||
case "StringLiteral":
|
||||
let value = expr.value;
|
||||
invariant(
|
||||
// ensure the 1st character of the string is lowercase
|
||||
// otherwise it will appear as a component
|
||||
value.length > 0 && value[0] === value[0].toLowerCase(),
|
||||
"invalid JSXIdentifer from string, strings must be lowercase"
|
||||
);
|
||||
return t.jSXIdentifier(value);
|
||||
case "MemberExpression":
|
||||
invariant(expr.computed === false, "Cannot inline computed expressions in JSX type.");
|
||||
return t.jSXMemberExpression(
|
||||
convertExpressionToJSXIdentifier(expr.object, false),
|
||||
((convertExpressionToJSXIdentifier(expr.property, false): any): BabelNodeJSXIdentifier)
|
||||
);
|
||||
default:
|
||||
invariant(false, "Invalid JSX type");
|
||||
}
|
||||
}
|
||||
|
||||
export function convertJSXExpressionToIdentifier(
|
||||
expr: BabelNodeExpression
|
||||
): BabelNodeMemberExpression | BabelNodeIdentifier {
|
||||
switch (expr.type) {
|
||||
case "JSXIdentifier":
|
||||
return t.identifier(expr.name);
|
||||
case "JSXMemberExpression":
|
||||
return t.memberExpression(
|
||||
convertJSXExpressionToIdentifier(expr.object),
|
||||
(convertJSXExpressionToIdentifier(expr.property): any)
|
||||
);
|
||||
default:
|
||||
invariant(false, "Invalid JSX type");
|
||||
}
|
||||
}
|
||||
|
||||
export function convertKeyValueToJSXAttribute(key: string, expr: BabelNodeExpression) {
|
||||
return t.jSXAttribute(t.jSXIdentifier(key), expr.type === "StringLiteral" ? expr : t.jSXExpressionContainer(expr));
|
||||
}
|
||||
|
||||
function addKeyToElement(astElement: BabelNodeJSXElement, key) {
|
||||
let astAttributes = astElement.openingElement.attributes;
|
||||
let existingKey = null;
|
||||
|
||||
for (let i = 0; i < astAttributes.length; i++) {
|
||||
let astAttribute = astAttributes[i];
|
||||
|
||||
if (t.isJSXAttribute(astAttribute) && t.isJSXIdentifier(astAttribute.name) && astAttribute.name.name === "key") {
|
||||
existingKey = astAttribute.value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (existingKey === null) {
|
||||
astAttributes.push(t.jSXAttribute(t.jSXIdentifier("key"), t.stringLiteral(key)));
|
||||
}
|
||||
}
|
||||
|
||||
// we create a unique key for each JSXElement to prevent collisions
|
||||
// otherwise React will detect a missing/conflicting key at runtime and
|
||||
// this can break the reconcilation of JSXElements in arrays
|
||||
function getUniqueJSXElementKey(index?: string, usedReactElementKeys: Set<string>) {
|
||||
let key;
|
||||
do {
|
||||
key = Math.random().toString(36).replace(/[^a-z]+/g, "").substring(0, 2);
|
||||
} while (usedReactElementKeys.has(key));
|
||||
usedReactElementKeys.add(key);
|
||||
if (index !== undefined) {
|
||||
return `${key}${index}`;
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
export function applyKeysToNestedArray(
|
||||
expr: BabelNodeArrayExpression,
|
||||
isBase: boolean,
|
||||
usedReactElementKeys: Set<string>
|
||||
): void {
|
||||
let astElements = expr.elements;
|
||||
|
||||
if (Array.isArray(astElements)) {
|
||||
for (let i = 0; i < astElements.length; i++) {
|
||||
let astElement = astElements[i];
|
||||
|
||||
if (astElement != null) {
|
||||
if (t.isJSXElement(astElement) && isBase === false) {
|
||||
addKeyToElement((astElement: any), getUniqueJSXElementKey("" + i, usedReactElementKeys));
|
||||
} else if (t.isArrayExpression(astElement)) {
|
||||
applyKeysToNestedArray((astElement: any), false, usedReactElementKeys);
|
||||
} else if (astElement.type === "ConditionalExpression") {
|
||||
let alternate = (astElement.alternate: any);
|
||||
// it's common for conditions to be in an array, which means we need to check them for keys too
|
||||
if (t.isJSXElement(alternate.type) && isBase === false) {
|
||||
addKeyToElement(alternate, getUniqueJSXElementKey("0" + i, usedReactElementKeys));
|
||||
} else if (t.isArrayExpression(alternate.type)) {
|
||||
applyKeysToNestedArray(alternate, false, usedReactElementKeys);
|
||||
}
|
||||
let consequent = (astElement.consequent: any);
|
||||
if (t.isJSXElement(consequent.type) && isBase === false) {
|
||||
addKeyToElement(consequent, getUniqueJSXElementKey("1" + i, usedReactElementKeys));
|
||||
} else if (t.isArrayExpression(consequent.type)) {
|
||||
applyKeysToNestedArray(consequent, false, usedReactElementKeys);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -27,7 +27,11 @@ export default function(
|
||||
startLine: number = 1
|
||||
): BabelNodeFile {
|
||||
try {
|
||||
let ast = parse(code, { filename, sourceType, startLine });
|
||||
let plugins = [];
|
||||
if (realm.react.enabled) {
|
||||
plugins.push("jsx", "flow");
|
||||
}
|
||||
let ast = parse(code, { filename, sourceType, startLine, plugins });
|
||||
traverseFast(ast, node => {
|
||||
invariant(node.loc);
|
||||
node.loc.source = filename;
|
||||
|
@ -34,6 +34,7 @@ import {
|
||||
UndefinedValue,
|
||||
Value,
|
||||
} from "./index.js";
|
||||
import { isReactElement } from "../utils/jsx";
|
||||
import type { ECMAScriptSourceFunctionValue, NativeFunctionCallback } from "./index.js";
|
||||
import {
|
||||
joinValuesAsConditional,
|
||||
@ -319,6 +320,7 @@ export default class ObjectValue extends ConcreteValue {
|
||||
if (this.$ArrayBufferData !== undefined) return "ArrayBuffer";
|
||||
if (this.$WeakMapData !== undefined) return "WeakMap";
|
||||
if (this.$WeakSetData !== undefined) return "WeakSet";
|
||||
if (isReactElement(this) && this.$Realm.react.enabled) return "ReactElement";
|
||||
if (this.$TypedArrayName !== undefined) return this.$TypedArrayName;
|
||||
// TODO #26 #712: Promises. All kinds of iterators. Generators.
|
||||
return "Object";
|
||||
|
61
test/serializer/react/default-props.js
Normal file
61
test/serializer/react/default-props.js
Normal file
@ -0,0 +1,61 @@
|
||||
// react
|
||||
// babel:jsx
|
||||
|
||||
function MyComponent(props) {
|
||||
return <span>{props.title}</span>;
|
||||
}
|
||||
MyComponent.defaultProps = {
|
||||
title: 'Hello world',
|
||||
children: 'No children!',
|
||||
};
|
||||
|
||||
function ChildComponent(props) {
|
||||
return <span>{props.title}</span>;
|
||||
}
|
||||
ChildComponent.defaultProps = {
|
||||
title: 'I am a child',
|
||||
};
|
||||
|
||||
function createElement(type, options, ...children) {
|
||||
let key = null;
|
||||
let ref = null;
|
||||
|
||||
if (options != null) {
|
||||
if (options.key !== undefined) {
|
||||
key = options.key;
|
||||
delete options.key;
|
||||
}
|
||||
if (options.ref !== undefined) {
|
||||
ref = options.ref;
|
||||
delete options.ref;
|
||||
}
|
||||
}
|
||||
let props = Object.assign({}, options);
|
||||
if (children !== undefined) {
|
||||
if (children.length === 1) {
|
||||
props.children = children[0];
|
||||
} else {
|
||||
props.children = children;
|
||||
}
|
||||
}
|
||||
return {
|
||||
$$typeof: Symbol.for('react.element'),
|
||||
props,
|
||||
key,
|
||||
ref,
|
||||
type,
|
||||
_owner: undefined,
|
||||
}
|
||||
}
|
||||
|
||||
global.React = {
|
||||
createElement,
|
||||
};
|
||||
|
||||
global.reactElement = (
|
||||
<MyComponent>
|
||||
<ChildComponent title={"I am a child (overwritten)"} />
|
||||
</MyComponent>
|
||||
);
|
||||
|
||||
inspect = function() { return reactElement; }
|
48
test/serializer/react/element-simple.js
Normal file
48
test/serializer/react/element-simple.js
Normal file
@ -0,0 +1,48 @@
|
||||
// react
|
||||
// babel:jsx
|
||||
|
||||
function MyComponent() {
|
||||
// ...
|
||||
}
|
||||
|
||||
function createElement(type, options, ...children) {
|
||||
let key = null;
|
||||
let ref = null;
|
||||
|
||||
if (options != null) {
|
||||
if (options.key !== undefined) {
|
||||
key = options.key;
|
||||
delete options.key;
|
||||
}
|
||||
if (options.ref !== undefined) {
|
||||
ref = options.ref;
|
||||
delete options.ref;
|
||||
}
|
||||
}
|
||||
let props = Object.assign({}, options);
|
||||
if (children !== undefined) {
|
||||
if (children.length === 1) {
|
||||
props.children = children[0];
|
||||
} else {
|
||||
props.children = children;
|
||||
}
|
||||
}
|
||||
return {
|
||||
$$typeof: Symbol.for('react.element'),
|
||||
props,
|
||||
key,
|
||||
ref,
|
||||
type,
|
||||
_owner: undefined,
|
||||
}
|
||||
}
|
||||
|
||||
global.React = {
|
||||
createElement,
|
||||
};
|
||||
|
||||
global.reactElement = createElement('div', null, createElement(MyComponent, {
|
||||
foo: 'bar'
|
||||
}, 'Hello world'));
|
||||
|
||||
inspect = function() { return reactElement; }
|
51
test/serializer/react/jsx-simple.js
Normal file
51
test/serializer/react/jsx-simple.js
Normal file
@ -0,0 +1,51 @@
|
||||
// react
|
||||
// babel:jsx
|
||||
|
||||
function MyComponent() {
|
||||
// ...
|
||||
}
|
||||
|
||||
const Container = {
|
||||
MyComponent,
|
||||
};
|
||||
|
||||
function createElement(type, options, ...children) {
|
||||
let key = null;
|
||||
let ref = null;
|
||||
|
||||
if (options != null) {
|
||||
if (options.key !== undefined) {
|
||||
key = options.key;
|
||||
delete options.key;
|
||||
}
|
||||
if (options.ref !== undefined) {
|
||||
ref = options.ref;
|
||||
delete options.ref;
|
||||
}
|
||||
}
|
||||
let props = Object.assign({}, options);
|
||||
if (children !== undefined) {
|
||||
if (children.length === 1) {
|
||||
props.children = children[0];
|
||||
} else {
|
||||
props.children = children;
|
||||
}
|
||||
}
|
||||
return {
|
||||
$$typeof: Symbol.for('react.element'),
|
||||
props,
|
||||
key,
|
||||
ref,
|
||||
type,
|
||||
_owner: undefined,
|
||||
}
|
||||
}
|
||||
|
||||
global.React = {
|
||||
createElement,
|
||||
};
|
||||
|
||||
global.reactElement = <div><MyComponent foo="bar">Hello world</MyComponent></div>;
|
||||
global.reactElementB = <div><Container.MyComponent foo="bar">Hello world</Container.MyComponent></div>;
|
||||
|
||||
inspect = function() { return [reactElement, reactElementB]; }
|
59
test/serializer/react/jsx-spread.js
Normal file
59
test/serializer/react/jsx-spread.js
Normal file
@ -0,0 +1,59 @@
|
||||
// react
|
||||
// babel:jsx
|
||||
|
||||
const Container = {
|
||||
MyComponent,
|
||||
};
|
||||
|
||||
function createElement(type, options, ...children) {
|
||||
let key = null;
|
||||
let ref = null;
|
||||
|
||||
if (options != null) {
|
||||
if (options.key !== undefined) {
|
||||
key = options.key;
|
||||
delete options.key;
|
||||
}
|
||||
if (options.ref !== undefined) {
|
||||
ref = options.ref;
|
||||
delete options.ref;
|
||||
}
|
||||
}
|
||||
let props = Object.assign({}, options);
|
||||
if (children !== undefined) {
|
||||
if (children.length === 1) {
|
||||
props.children = children[0];
|
||||
} else {
|
||||
props.children = children;
|
||||
}
|
||||
}
|
||||
return {
|
||||
$$typeof: Symbol.for('react.element'),
|
||||
props,
|
||||
key,
|
||||
ref,
|
||||
type,
|
||||
_owner: undefined,
|
||||
}
|
||||
}
|
||||
|
||||
global.React = {
|
||||
createElement,
|
||||
};
|
||||
|
||||
function MyComponent(props) {
|
||||
return <span>Title: {props.title}, Number: {props.number}</span>;
|
||||
}
|
||||
|
||||
let props = {
|
||||
title: "Hello world",
|
||||
number: 50,
|
||||
};
|
||||
|
||||
global.reactElement = (
|
||||
<div>
|
||||
<MyComponent {...props}>Hello world</MyComponent>
|
||||
</div>
|
||||
);
|
||||
|
||||
inspect = function() { return reactElement; }
|
64
test/serializer/react/key-children.js
Normal file
64
test/serializer/react/key-children.js
Normal file
@ -0,0 +1,64 @@
|
||||
// react
|
||||
// babel:jsx
|
||||
|
||||
function createElement(type, options, ...children) {
|
||||
let key = null;
|
||||
let ref = null;
|
||||
|
||||
if (options != null) {
|
||||
if (options.key !== undefined) {
|
||||
key = options.key;
|
||||
delete options.key;
|
||||
}
|
||||
if (options.ref !== undefined) {
|
||||
ref = options.ref;
|
||||
delete options.ref;
|
||||
}
|
||||
}
|
||||
let props = Object.assign({}, options);
|
||||
if (children !== undefined) {
|
||||
if (children.length === 1) {
|
||||
props.children = children[0];
|
||||
} else {
|
||||
props.children = children;
|
||||
}
|
||||
}
|
||||
return {
|
||||
$$typeof: Symbol.for('react.element'),
|
||||
props,
|
||||
key,
|
||||
ref,
|
||||
type,
|
||||
_owner: undefined,
|
||||
}
|
||||
}
|
||||
|
||||
global.React = {
|
||||
createElement,
|
||||
};
|
||||
|
||||
global.toggle = true;
|
||||
|
||||
global.reactElement = (
|
||||
<div>
|
||||
<span>Span 1</span>
|
||||
Text node
|
||||
{
|
||||
[
|
||||
<span>Span 2</span>,
|
||||
"Text node",
|
||||
[
|
||||
<em key="a">Em 1</em>,
|
||||
<em key="b">Em 2</em>,
|
||||
"Text node",
|
||||
<em key="c">Em 3</em>,
|
||||
toggle ? <em key="d">Em 4</em> : <em key="d">Em 5</em>,
|
||||
],
|
||||
<span>Span 3</span>,
|
||||
]
|
||||
}
|
||||
<span>Span 4</span>
|
||||
</div>
|
||||
);
|
||||
|
||||
inspect = function() { return reactElement; }
|
133
yarn.lock
133
yarn.lock
@ -272,6 +272,14 @@ babel-code-frame@^6.16.0, babel-code-frame@^6.22.0:
|
||||
esutils "^2.0.2"
|
||||
js-tokens "^3.0.0"
|
||||
|
||||
babel-code-frame@^6.26.0:
|
||||
version "6.26.0"
|
||||
resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b"
|
||||
dependencies:
|
||||
chalk "^1.1.3"
|
||||
esutils "^2.0.2"
|
||||
js-tokens "^3.0.2"
|
||||
|
||||
babel-core@^5.8.33:
|
||||
version "5.8.38"
|
||||
resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-5.8.38.tgz#1fcaee79d7e61b750b00b8e54f6dfc9d0af86558"
|
||||
@ -323,7 +331,7 @@ babel-core@^5.8.33:
|
||||
trim-right "^1.0.0"
|
||||
try-resolve "^1.0.0"
|
||||
|
||||
babel-core@^6.24.1, babel-core@^6.8.0:
|
||||
babel-core@^6.24.1:
|
||||
version "6.25.0"
|
||||
resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-6.25.0.tgz#7dd42b0463c742e9d5296deb3ec67a9322dad729"
|
||||
dependencies:
|
||||
@ -347,6 +355,30 @@ babel-core@^6.24.1, babel-core@^6.8.0:
|
||||
slash "^1.0.0"
|
||||
source-map "^0.5.0"
|
||||
|
||||
babel-core@^6.26.0:
|
||||
version "6.26.0"
|
||||
resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-6.26.0.tgz#af32f78b31a6fcef119c87b0fd8d9753f03a0bb8"
|
||||
dependencies:
|
||||
babel-code-frame "^6.26.0"
|
||||
babel-generator "^6.26.0"
|
||||
babel-helpers "^6.24.1"
|
||||
babel-messages "^6.23.0"
|
||||
babel-register "^6.26.0"
|
||||
babel-runtime "^6.26.0"
|
||||
babel-template "^6.26.0"
|
||||
babel-traverse "^6.26.0"
|
||||
babel-types "^6.26.0"
|
||||
babylon "^6.18.0"
|
||||
convert-source-map "^1.5.0"
|
||||
debug "^2.6.8"
|
||||
json5 "^0.5.1"
|
||||
lodash "^4.17.4"
|
||||
minimatch "^3.0.4"
|
||||
path-is-absolute "^1.0.1"
|
||||
private "^0.1.7"
|
||||
slash "^1.0.0"
|
||||
source-map "^0.5.6"
|
||||
|
||||
babel-eslint@^4.1.8:
|
||||
version "4.1.8"
|
||||
resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-4.1.8.tgz#4f79e7a4f5879ecf03f48cb16f552a355fcc31b2"
|
||||
@ -378,6 +410,19 @@ babel-generator@^6.25.0, babel-generator@^6.8.0:
|
||||
source-map "^0.5.0"
|
||||
trim-right "^1.0.1"
|
||||
|
||||
babel-generator@^6.26.0:
|
||||
version "6.26.0"
|
||||
resolved "https://registry.yarnpkg.com/babel-generator/-/babel-generator-6.26.0.tgz#ac1ae20070b79f6e3ca1d3269613053774f20dc5"
|
||||
dependencies:
|
||||
babel-messages "^6.23.0"
|
||||
babel-runtime "^6.26.0"
|
||||
babel-types "^6.26.0"
|
||||
detect-indent "^4.0.0"
|
||||
jsesc "^1.3.0"
|
||||
lodash "^4.17.4"
|
||||
source-map "^0.5.6"
|
||||
trim-right "^1.0.1"
|
||||
|
||||
babel-helper-builder-binary-assignment-operator-visitor@^6.24.1:
|
||||
version "6.24.1"
|
||||
resolved "https://registry.yarnpkg.com/babel-helper-builder-binary-assignment-operator-visitor/-/babel-helper-builder-binary-assignment-operator-visitor-6.24.1.tgz#cce4517ada356f4220bcae8a02c2b346f9a56664"
|
||||
@ -911,6 +956,18 @@ babel-register@^6.24.1:
|
||||
mkdirp "^0.5.1"
|
||||
source-map-support "^0.4.2"
|
||||
|
||||
babel-register@^6.26.0:
|
||||
version "6.26.0"
|
||||
resolved "https://registry.yarnpkg.com/babel-register/-/babel-register-6.26.0.tgz#6ed021173e2fcb486d7acb45c6009a856f647071"
|
||||
dependencies:
|
||||
babel-core "^6.26.0"
|
||||
babel-runtime "^6.26.0"
|
||||
core-js "^2.5.0"
|
||||
home-or-tmp "^2.0.0"
|
||||
lodash "^4.17.4"
|
||||
mkdirp "^0.5.1"
|
||||
source-map-support "^0.4.15"
|
||||
|
||||
babel-runtime@^6.18.0, babel-runtime@^6.22.0:
|
||||
version "6.23.0"
|
||||
resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.23.0.tgz#0a9489f144de70efb3ce4300accdb329e2fc543b"
|
||||
@ -918,6 +975,13 @@ babel-runtime@^6.18.0, babel-runtime@^6.22.0:
|
||||
core-js "^2.4.0"
|
||||
regenerator-runtime "^0.10.0"
|
||||
|
||||
babel-runtime@^6.26.0:
|
||||
version "6.26.0"
|
||||
resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe"
|
||||
dependencies:
|
||||
core-js "^2.4.0"
|
||||
regenerator-runtime "^0.11.0"
|
||||
|
||||
babel-template@^6.24.1, babel-template@^6.25.0, babel-template@^6.9.0:
|
||||
version "6.25.0"
|
||||
resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.25.0.tgz#665241166b7c2aa4c619d71e192969552b10c071"
|
||||
@ -928,6 +992,16 @@ babel-template@^6.24.1, babel-template@^6.25.0, babel-template@^6.9.0:
|
||||
babylon "^6.17.2"
|
||||
lodash "^4.2.0"
|
||||
|
||||
babel-template@^6.26.0:
|
||||
version "6.26.0"
|
||||
resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.26.0.tgz#de03e2d16396b069f46dd9fff8521fb1a0e35e02"
|
||||
dependencies:
|
||||
babel-runtime "^6.26.0"
|
||||
babel-traverse "^6.26.0"
|
||||
babel-types "^6.26.0"
|
||||
babylon "^6.18.0"
|
||||
lodash "^4.17.4"
|
||||
|
||||
babel-traverse@^6.23.1, babel-traverse@^6.24.1, babel-traverse@^6.25.0, babel-traverse@^6.9.0:
|
||||
version "6.25.0"
|
||||
resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.25.0.tgz#2257497e2fcd19b89edc13c4c91381f9512496f1"
|
||||
@ -942,6 +1016,20 @@ babel-traverse@^6.23.1, babel-traverse@^6.24.1, babel-traverse@^6.25.0, babel-tr
|
||||
invariant "^2.2.0"
|
||||
lodash "^4.2.0"
|
||||
|
||||
babel-traverse@^6.26.0:
|
||||
version "6.26.0"
|
||||
resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.26.0.tgz#46a9cbd7edcc62c8e5c064e2d2d8d0f4035766ee"
|
||||
dependencies:
|
||||
babel-code-frame "^6.26.0"
|
||||
babel-messages "^6.23.0"
|
||||
babel-runtime "^6.26.0"
|
||||
babel-types "^6.26.0"
|
||||
babylon "^6.18.0"
|
||||
debug "^2.6.8"
|
||||
globals "^9.18.0"
|
||||
invariant "^2.2.2"
|
||||
lodash "^4.17.4"
|
||||
|
||||
babel-types@^6.19.0, babel-types@^6.23.0, babel-types@^6.24.1, babel-types@^6.25.0, babel-types@^6.9.0:
|
||||
version "6.25.0"
|
||||
resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.25.0.tgz#70afb248d5660e5d18f811d91c8303b54134a18e"
|
||||
@ -951,6 +1039,15 @@ babel-types@^6.19.0, babel-types@^6.23.0, babel-types@^6.24.1, babel-types@^6.25
|
||||
lodash "^4.2.0"
|
||||
to-fast-properties "^1.0.1"
|
||||
|
||||
babel-types@^6.26.0:
|
||||
version "6.26.0"
|
||||
resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.26.0.tgz#a3b073f94ab49eb6fa55cd65227a334380632497"
|
||||
dependencies:
|
||||
babel-runtime "^6.26.0"
|
||||
esutils "^2.0.2"
|
||||
lodash "^4.17.4"
|
||||
to-fast-properties "^1.0.3"
|
||||
|
||||
babylon@^5.8.38:
|
||||
version "5.8.38"
|
||||
resolved "https://registry.yarnpkg.com/babylon/-/babylon-5.8.38.tgz#ec9b120b11bf6ccd4173a18bf217e60b79859ffd"
|
||||
@ -1386,7 +1483,7 @@ content-type-parser@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/content-type-parser/-/content-type-parser-1.0.1.tgz#c3e56988c53c65127fb46d4032a3a900246fdc94"
|
||||
|
||||
convert-source-map@^1.1.0, convert-source-map@^1.1.1:
|
||||
convert-source-map@^1.1.0, convert-source-map@^1.1.1, convert-source-map@^1.5.0:
|
||||
version "1.5.0"
|
||||
resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.5.0.tgz#9acd70851c6d5dfdd93d9282e5edf94a03ff46b5"
|
||||
|
||||
@ -1398,6 +1495,10 @@ core-js@^2.4.0:
|
||||
version "2.4.1"
|
||||
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.4.1.tgz#4de911e667b0eae9124e34254b53aea6fc618d3e"
|
||||
|
||||
core-js@^2.5.0:
|
||||
version "2.5.1"
|
||||
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.1.tgz#ae6874dc66937789b80754ff5428df66819ca50b"
|
||||
|
||||
core-util-is@~1.0.0:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
|
||||
@ -1505,6 +1606,12 @@ debug@^2.1.1, debug@^2.2.0:
|
||||
dependencies:
|
||||
ms "2.0.0"
|
||||
|
||||
debug@^2.6.8:
|
||||
version "2.6.9"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
|
||||
dependencies:
|
||||
ms "2.0.0"
|
||||
|
||||
debug@~2.2.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-2.2.0.tgz#f87057e995b1a1f6ae6a4960664137bc56f039da"
|
||||
@ -2487,7 +2594,7 @@ globals@^8.18.0:
|
||||
version "8.18.0"
|
||||
resolved "https://registry.yarnpkg.com/globals/-/globals-8.18.0.tgz#93d4a62bdcac38cfafafc47d6b034768cb0ffcb4"
|
||||
|
||||
globals@^9.0.0, globals@^9.14.0:
|
||||
globals@^9.0.0, globals@^9.14.0, globals@^9.18.0:
|
||||
version "9.18.0"
|
||||
resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a"
|
||||
|
||||
@ -3065,7 +3172,7 @@ js-tokens@1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-1.0.1.tgz#cc435a5c8b94ad15acb7983140fc80182c89aeae"
|
||||
|
||||
js-tokens@^3.0.0:
|
||||
js-tokens@^3.0.0, js-tokens@^3.0.2:
|
||||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b"
|
||||
|
||||
@ -3394,7 +3501,7 @@ lodash@^3.10.0, lodash@^3.9.3:
|
||||
version "3.10.1"
|
||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.10.1.tgz#5bf45e8e49ba4189e17d482789dfd15bd140b7b6"
|
||||
|
||||
lodash@^4.0.0, lodash@^4.14.0, lodash@^4.15.0, lodash@^4.2.0, lodash@^4.3.0:
|
||||
lodash@^4.0.0, lodash@^4.14.0, lodash@^4.15.0, lodash@^4.17.4, lodash@^4.2.0, lodash@^4.3.0:
|
||||
version "4.17.4"
|
||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae"
|
||||
|
||||
@ -3903,7 +4010,7 @@ path-exists@^2.0.0:
|
||||
dependencies:
|
||||
pinkie-promise "^2.0.0"
|
||||
|
||||
path-is-absolute@^1.0.0:
|
||||
path-is-absolute@^1.0.0, path-is-absolute@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
|
||||
|
||||
@ -4007,7 +4114,7 @@ pretty-ms@2.1.0:
|
||||
parse-ms "^1.0.0"
|
||||
plur "^1.0.0"
|
||||
|
||||
private@^0.1.6, private@~0.1.5:
|
||||
private@^0.1.6, private@^0.1.7, private@~0.1.5:
|
||||
version "0.1.7"
|
||||
resolved "https://registry.yarnpkg.com/private/-/private-0.1.7.tgz#68ce5e8a1ef0a23bb570cc28537b5332aba63ef1"
|
||||
|
||||
@ -4211,6 +4318,10 @@ regenerator-runtime@^0.10.0:
|
||||
version "0.10.5"
|
||||
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz#336c3efc1220adcedda2c9fab67b5a7955a33658"
|
||||
|
||||
regenerator-runtime@^0.11.0:
|
||||
version "0.11.0"
|
||||
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.0.tgz#7e54fe5b5ccd5d6624ea6255c3473be090b802e1"
|
||||
|
||||
regenerator-transform@0.9.11:
|
||||
version "0.9.11"
|
||||
resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.9.11.tgz#3a7d067520cb7b7176769eb5ff868691befe1283"
|
||||
@ -4540,6 +4651,12 @@ source-map-support@^0.2.10:
|
||||
dependencies:
|
||||
source-map "0.1.32"
|
||||
|
||||
source-map-support@^0.4.15:
|
||||
version "0.4.18"
|
||||
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.18.tgz#0286a6de8be42641338594e97ccea75f0a2c585f"
|
||||
dependencies:
|
||||
source-map "^0.5.6"
|
||||
|
||||
source-map-support@^0.4.2, source-map-support@^0.4.6:
|
||||
version "0.4.15"
|
||||
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.15.tgz#03202df65c06d2bd8c7ec2362a193056fef8d3b1"
|
||||
@ -4898,7 +5015,7 @@ to-arraybuffer@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43"
|
||||
|
||||
to-fast-properties@^1.0.0, to-fast-properties@^1.0.1:
|
||||
to-fast-properties@^1.0.0, to-fast-properties@^1.0.1, to-fast-properties@^1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47"
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user