mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Optimize the transformed output of JSXSpreadAttributes containing an ObjectLiteralExpression (#49100)
This commit is contained in:
@@ -35,8 +35,12 @@ import {
|
||||
isJsxSelfClosingElement,
|
||||
isJsxSpreadAttribute,
|
||||
isLineBreak,
|
||||
isObjectLiteralExpression,
|
||||
isPropertyAssignment,
|
||||
isSourceFile,
|
||||
isSpreadAssignment,
|
||||
isStringDoubleQuoted,
|
||||
isStringLiteral,
|
||||
isWhiteSpaceSingleLine,
|
||||
JsxAttribute,
|
||||
JsxAttributeValue,
|
||||
@@ -55,6 +59,8 @@ import {
|
||||
mapDefined,
|
||||
Node,
|
||||
NodeFlags,
|
||||
ObjectLiteralElementLike,
|
||||
ObjectLiteralExpression,
|
||||
PropertyAssignment,
|
||||
ScriptTarget,
|
||||
setIdentifierGeneratedImportReference,
|
||||
@@ -63,7 +69,6 @@ import {
|
||||
singleOrUndefined,
|
||||
SourceFile,
|
||||
spanMap,
|
||||
SpreadAssignment,
|
||||
startOnNewLine,
|
||||
Statement,
|
||||
StringLiteral,
|
||||
@@ -243,13 +248,18 @@ export function transformJsx(context: TransformationContext): (x: SourceFile | B
|
||||
}
|
||||
}
|
||||
|
||||
function hasProto(obj: ObjectLiteralExpression) {
|
||||
return obj.properties.some(p => isPropertyAssignment(p) &&
|
||||
(isIdentifier(p.name) && idText(p.name) === "__proto__" || isStringLiteral(p.name) && p.name.text === "__proto__"));
|
||||
}
|
||||
|
||||
/**
|
||||
* The react jsx/jsxs transform falls back to `createElement` when an explicit `key` argument comes after a spread
|
||||
*/
|
||||
function hasKeyAfterPropsSpread(node: JsxOpeningLikeElement) {
|
||||
let spread = false;
|
||||
for (const elem of node.attributes.properties) {
|
||||
if (isJsxSpreadAttribute(elem)) {
|
||||
if (isJsxSpreadAttribute(elem) && (!isObjectLiteralExpression(elem.expression) || elem.expression.properties.some(isSpreadAssignment))) {
|
||||
spread = true;
|
||||
}
|
||||
else if (spread && isJsxAttribute(elem) && elem.name.escapedText === "key") {
|
||||
@@ -427,7 +437,10 @@ export function transformJsx(context: TransformationContext): (x: SourceFile | B
|
||||
return element;
|
||||
}
|
||||
|
||||
function transformJsxSpreadAttributeToSpreadAssignment(node: JsxSpreadAttribute) {
|
||||
function transformJsxSpreadAttributeToProps(node: JsxSpreadAttribute) {
|
||||
if (isObjectLiteralExpression(node.expression) && !hasProto(node.expression)) {
|
||||
return node.expression.properties;
|
||||
}
|
||||
return factory.createSpreadAssignment(Debug.checkDefined(visitNode(node.expression, visitor, isExpression)));
|
||||
}
|
||||
|
||||
@@ -438,8 +451,8 @@ export function transformJsx(context: TransformationContext): (x: SourceFile | B
|
||||
}
|
||||
|
||||
function transformJsxAttributesToProps(attrs: readonly(JsxSpreadAttribute | JsxAttribute)[], children?: PropertyAssignment) {
|
||||
const props = flatten<SpreadAssignment | PropertyAssignment>(spanMap(attrs, isJsxSpreadAttribute, (attrs, isSpread) =>
|
||||
map(attrs, attr => isSpread ? transformJsxSpreadAttributeToSpreadAssignment(attr as JsxSpreadAttribute) : transformJsxAttributeToObjectLiteralElement(attr as JsxAttribute))));
|
||||
const props = flatten(spanMap(attrs, isJsxSpreadAttribute, (attrs, isSpread) =>
|
||||
flatten(map(attrs, attr => isSpread ? transformJsxSpreadAttributeToProps(attr as JsxSpreadAttribute) : transformJsxAttributeToObjectLiteralElement(attr as JsxAttribute)))));
|
||||
if (children) {
|
||||
props.push(children);
|
||||
}
|
||||
@@ -447,30 +460,52 @@ export function transformJsx(context: TransformationContext): (x: SourceFile | B
|
||||
}
|
||||
|
||||
function transformJsxAttributesToExpression(attrs: readonly(JsxSpreadAttribute | JsxAttribute)[], children?: PropertyAssignment) {
|
||||
// Map spans of JsxAttribute nodes into object literals and spans
|
||||
// of JsxSpreadAttribute nodes into expressions.
|
||||
const expressions = flatten(
|
||||
spanMap(attrs, isJsxSpreadAttribute, (attrs, isSpread) => isSpread
|
||||
? map(attrs as JsxSpreadAttribute[], transformJsxSpreadAttributeToExpression)
|
||||
: factory.createObjectLiteralExpression(map(attrs as JsxAttribute[], transformJsxAttributeToObjectLiteralElement))
|
||||
)
|
||||
);
|
||||
const expressions: Expression[] = [];
|
||||
let properties: ObjectLiteralElementLike[] = [];
|
||||
|
||||
if (isJsxSpreadAttribute(attrs[0])) {
|
||||
// We must always emit at least one object literal before a spread
|
||||
// argument.factory.createObjectLiteral
|
||||
expressions.unshift(factory.createObjectLiteralExpression());
|
||||
for (const attr of attrs) {
|
||||
if (isJsxSpreadAttribute(attr)) {
|
||||
// as an optimization we try to flatten the first level of spread inline object
|
||||
// as if its props would be passed as JSX attributes
|
||||
if (isObjectLiteralExpression(attr.expression) && !hasProto(attr.expression)) {
|
||||
for (const prop of attr.expression.properties) {
|
||||
if (isSpreadAssignment(prop)) {
|
||||
finishObjectLiteralIfNeeded();
|
||||
expressions.push(prop.expression);
|
||||
continue;
|
||||
}
|
||||
properties.push(prop);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
finishObjectLiteralIfNeeded();
|
||||
expressions.push(attr.expression);
|
||||
continue;
|
||||
}
|
||||
properties.push(transformJsxAttributeToObjectLiteralElement(attr));
|
||||
}
|
||||
|
||||
if (children) {
|
||||
expressions.push(factory.createObjectLiteralExpression([children]));
|
||||
properties.push(children);
|
||||
}
|
||||
|
||||
finishObjectLiteralIfNeeded();
|
||||
|
||||
if (expressions.length && !isObjectLiteralExpression(expressions[0])) {
|
||||
// We must always emit at least one object literal before a spread attribute
|
||||
// as the JSX always factory expects a fresh object, so we need to make a copy here
|
||||
// we also avoid mutating an external reference by doing this (first expression is used as assign's target)
|
||||
expressions.unshift(factory.createObjectLiteralExpression());
|
||||
}
|
||||
|
||||
return singleOrUndefined(expressions) || emitHelpers().createAssignHelper(expressions);
|
||||
}
|
||||
|
||||
function transformJsxSpreadAttributeToExpression(node: JsxSpreadAttribute) {
|
||||
return Debug.checkDefined(visitNode(node.expression, visitor, isExpression));
|
||||
function finishObjectLiteralIfNeeded() {
|
||||
if (properties.length) {
|
||||
expressions.push(factory.createObjectLiteralExpression(properties));
|
||||
properties = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function transformJsxAttributeToObjectLiteralElement(node: JsxAttribute) {
|
||||
|
||||
Reference in New Issue
Block a user