@@ -170,7 +170,7 @@ namespace ts {
170
170
function hasKeyAfterPropsSpread ( node : JsxOpeningLikeElement ) {
171
171
let spread = false ;
172
172
for ( const elem of node . attributes . properties ) {
173
- if ( isJsxSpreadAttribute ( elem ) ) {
173
+ if ( isJsxSpreadAttribute ( elem ) && ( ! isObjectLiteralExpression ( elem . expression ) || elem . expression . properties . some ( isSpreadAssignment ) ) ) {
174
174
spread = true ;
175
175
}
176
176
else if ( spread && isJsxAttribute ( elem ) && elem . name . escapedText === "key" ) {
@@ -348,7 +348,10 @@ namespace ts {
348
348
return element ;
349
349
}
350
350
351
- function transformJsxSpreadAttributeToSpreadAssignment ( node : JsxSpreadAttribute ) {
351
+ function transformJsxSpreadAttributeToProps ( node : JsxSpreadAttribute ) {
352
+ if ( isObjectLiteralExpression ( node . expression ) ) {
353
+ return node . expression . properties ;
354
+ }
352
355
return factory . createSpreadAssignment ( visitNode ( node . expression , visitor , isExpression ) ) ;
353
356
}
354
357
@@ -359,39 +362,61 @@ namespace ts {
359
362
}
360
363
361
364
function transformJsxAttributesToProps ( attrs : readonly ( JsxSpreadAttribute | JsxAttribute ) [ ] , children ?: PropertyAssignment ) {
362
- const props = flatten < SpreadAssignment | PropertyAssignment > ( spanMap ( attrs , isJsxSpreadAttribute , ( attrs , isSpread ) =>
363
- map ( attrs , attr => isSpread ? transformJsxSpreadAttributeToSpreadAssignment ( attr as JsxSpreadAttribute ) : transformJsxAttributeToObjectLiteralElement ( attr as JsxAttribute ) ) ) ) ;
365
+ const props = flatten ( spanMap ( attrs , isJsxSpreadAttribute , ( attrs , isSpread ) =>
366
+ flatten ( map ( attrs , attr => isSpread ? transformJsxSpreadAttributeToProps ( attr as JsxSpreadAttribute ) : transformJsxAttributeToObjectLiteralElement ( attr as JsxAttribute ) ) ) ) ) ;
364
367
if ( children ) {
365
368
props . push ( children ) ;
366
369
}
367
370
return props ;
368
371
}
369
372
370
373
function transformJsxAttributesToExpression ( attrs : readonly ( JsxSpreadAttribute | JsxAttribute ) [ ] , children ?: PropertyAssignment ) {
371
- // Map spans of JsxAttribute nodes into object literals and spans
372
- // of JsxSpreadAttribute nodes into expressions.
373
- const expressions = flatten (
374
- spanMap ( attrs , isJsxSpreadAttribute , ( attrs , isSpread ) => isSpread
375
- ? map ( attrs , transformJsxSpreadAttributeToExpression )
376
- : factory . createObjectLiteralExpression ( map ( attrs , transformJsxAttributeToObjectLiteralElement ) )
377
- )
378
- ) ;
379
-
380
- if ( isJsxSpreadAttribute ( attrs [ 0 ] ) ) {
381
- // We must always emit at least one object literal before a spread
382
- // argument.factory.createObjectLiteral
383
- expressions . unshift ( factory . createObjectLiteralExpression ( ) ) ;
374
+ const expressions : Expression [ ] = [ ] ;
375
+ let properties : ObjectLiteralElementLike [ ] = [ ] ;
376
+
377
+ for ( const attr of attrs ) {
378
+ if ( isJsxSpreadAttribute ( attr ) ) {
379
+ // as an optimization we try to flatten the first level of spread inline object
380
+ // as if its props would be passed as JSX attributes
381
+ if ( isObjectLiteralExpression ( attr . expression ) ) {
382
+ for ( const prop of attr . expression . properties ) {
383
+ if ( isSpreadAssignment ( prop ) ) {
384
+ finishObjectLiteralIfNeeded ( ) ;
385
+ expressions . push ( prop . expression ) ;
386
+ continue ;
387
+ }
388
+ properties . push ( prop ) ;
389
+ }
390
+ continue ;
391
+ }
392
+ finishObjectLiteralIfNeeded ( ) ;
393
+ expressions . push ( attr . expression ) ;
394
+ continue ;
395
+ }
396
+ properties . push ( transformJsxAttributeToObjectLiteralElement ( attr ) ) ;
384
397
}
385
398
386
399
if ( children ) {
387
- expressions . push ( factory . createObjectLiteralExpression ( [ children ] ) ) ;
400
+ properties . push ( children ) ;
401
+ }
402
+
403
+ finishObjectLiteralIfNeeded ( ) ;
404
+
405
+ if ( expressions . length && ! isObjectLiteralExpression ( expressions [ 0 ] ) ) {
406
+ // We must always emit at least one object literal before a spread attribute
407
+ // as the JSX always factory expects a fresh object, so we need to make a copy here
408
+ // we also avoid mutating an external reference by doing this (first expression is used as assign's target)
409
+ expressions . unshift ( factory . createObjectLiteralExpression ( ) ) ;
388
410
}
389
411
390
412
return singleOrUndefined ( expressions ) || emitHelpers ( ) . createAssignHelper ( expressions ) ;
391
- }
392
413
393
- function transformJsxSpreadAttributeToExpression ( node : JsxSpreadAttribute ) {
394
- return visitNode ( node . expression , visitor , isExpression ) ;
414
+ function finishObjectLiteralIfNeeded ( ) {
415
+ if ( properties . length ) {
416
+ expressions . push ( factory . createObjectLiteralExpression ( properties ) ) ;
417
+ properties = [ ] ;
418
+ }
419
+ }
395
420
}
396
421
397
422
function transformJsxAttributeToObjectLiteralElement ( node : JsxAttribute ) {
0 commit comments