@@ -49,6 +49,14 @@ var JSX_ATTRIBUTE_TRANSFORMS = {
4949 }
5050} ;
5151
52+ /**
53+ * Removes all non-whitespace/parenthesis characters
54+ */
55+ var reNonWhiteParen = / ( [ ^ \s \( \) ] ) / g;
56+ function stripNonWhiteParen ( value ) {
57+ return value . replace ( reNonWhiteParen , '' ) ;
58+ }
59+
5260function visitReactTag ( traverse , object , path , state ) {
5361 var jsxObjIdent = utils . getDocblock ( state ) . jsx ;
5462 var openingElement = object . openingElement ;
@@ -57,6 +65,7 @@ function visitReactTag(traverse, object, path, state) {
5765
5866 utils . catchup ( openingElement . range [ 0 ] , state , trimLeft ) ;
5967
68+
6069 if ( nameObject . type === Syntax . XJSNamespacedName && nameObject . namespace ) {
6170 throw new Error ( 'Namespace tags are not supported. ReactJSX is not XML.' ) ;
6271 }
@@ -75,23 +84,74 @@ function visitReactTag(traverse, object, path, state) {
7584
7685 var hasAttributes = attributesObject . length ;
7786
87+ var hasAtLeastOneSpreadProperty = attributesObject . some ( function ( attr ) {
88+ return attr . type === Syntax . XJSSpreadAttribute ;
89+ } ) ;
90+
7891 // if we don't have any attributes, pass in null
79- if ( hasAttributes ) {
92+ if ( hasAtLeastOneSpreadProperty ) {
93+ utils . append ( 'Object.assign({' , state ) ;
94+ } else if ( hasAttributes ) {
8095 utils . append ( '{' , state ) ;
8196 } else {
8297 utils . append ( 'null' , state ) ;
8398 }
8499
100+ // keep track of if the previous attribute was a spread attribute
101+ var previousWasSpread = false ;
102+
85103 // write attributes
86104 attributesObject . forEach ( function ( attr , index ) {
105+ var isLast = index === attributesObject . length - 1 ;
106+
107+ if ( attr . type === Syntax . XJSSpreadAttribute ) {
108+ // Plus 1 to skip `{`.
109+ utils . move ( attr . range [ 0 ] + 1 , state ) ;
110+
111+ // Close the previous object or initial object
112+ if ( ! previousWasSpread ) {
113+ utils . append ( '}, ' , state ) ;
114+ }
115+
116+ // Move to the expression start, ignoring everything except parenthesis
117+ // and whitespace.
118+ utils . catchup ( attr . argument . range [ 0 ] , state , stripNonWhiteParen ) ;
119+
120+ traverse ( attr . argument , path , state ) ;
121+
122+ utils . catchup ( attr . argument . range [ 1 ] , state ) ;
123+
124+ // Move to the end, ignoring parenthesis and the closing ` }`
125+ utils . catchup ( attr . range [ 1 ] - 1 , state , stripNonWhiteParen ) ;
126+
127+ if ( ! isLast ) {
128+ utils . append ( ', ' , state ) ;
129+ }
130+
131+ utils . move ( attr . range [ 1 ] , state ) ;
132+
133+ previousWasSpread = true ;
134+
135+ return ;
136+ }
137+
138+ // If the next attribute is a spread, we're effective last in this object
139+ if ( ! isLast ) {
140+ isLast = attributesObject [ index + 1 ] . type === Syntax . XJSSpreadAttribute ;
141+ }
142+
87143 if ( attr . name . namespace ) {
88144 throw new Error (
89145 'Namespace attributes are not supported. ReactJSX is not XML.' ) ;
90146 }
91147 var name = attr . name . name ;
92- var isLast = index === attributesObject . length - 1 ;
93148
94149 utils . catchup ( attr . range [ 0 ] , state , trimLeft ) ;
150+
151+ if ( previousWasSpread ) {
152+ utils . append ( '{' , state ) ;
153+ }
154+
95155 utils . append ( quoteAttrName ( name ) , state ) ;
96156 utils . append ( ': ' , state ) ;
97157
@@ -119,17 +179,24 @@ function visitReactTag(traverse, object, path, state) {
119179 }
120180
121181 utils . catchup ( attr . range [ 1 ] , state , trimLeft ) ;
182+
183+ previousWasSpread = false ;
184+
122185 } ) ;
123186
124187 if ( ! openingElement . selfClosing ) {
125188 utils . catchup ( openingElement . range [ 1 ] - 1 , state , trimLeft ) ;
126189 utils . move ( openingElement . range [ 1 ] , state ) ;
127190 }
128191
129- if ( hasAttributes ) {
192+ if ( hasAttributes && ! previousWasSpread ) {
130193 utils . append ( '}' , state ) ;
131194 }
132195
196+ if ( hasAtLeastOneSpreadProperty ) {
197+ utils . append ( ')' , state ) ;
198+ }
199+
133200 // filter out whitespace
134201 var childrenToRender = object . children . filter ( function ( child ) {
135202 return ! ( child . type === Syntax . Literal
0 commit comments