@@ -17,7 +17,8 @@ namespace ts {
1717 node : BinaryExpression ,
1818 needsValue : boolean ,
1919 recordTempVariable : ( node : Identifier ) => void ,
20- visitor ?: ( node : Node ) => VisitResult < Node > ) : Expression {
20+ visitor ?: ( node : Node ) => VisitResult < Node > ,
21+ transformRest ?: boolean ) : Expression {
2122
2223 if ( isEmptyObjectLiteralOrArrayLiteral ( node . left ) ) {
2324 const right = node . right ;
@@ -51,7 +52,7 @@ namespace ts {
5152 location = value ;
5253 }
5354
54- flattenDestructuring ( node , value , location , emitAssignment , emitTempVariableAssignment , visitor ) ;
55+ flattenDestructuring ( node , value , location , emitAssignment , emitTempVariableAssignment , emitRestAssignment , transformRest , visitor ) ;
5556
5657 if ( needsValue ) {
5758 expressions . push ( value ) ;
@@ -61,7 +62,7 @@ namespace ts {
6162 aggregateTransformFlags ( expression ) ;
6263 return expression ;
6364
64- function emitAssignment ( name : Identifier , value : Expression , location : TextRange ) {
65+ function emitAssignment ( name : Identifier | ObjectLiteralExpression , value : Expression , location : TextRange ) {
6566 const expression = createAssignment ( name , value , location ) ;
6667
6768 // NOTE: this completely disables source maps, but aligns with the behavior of
@@ -77,6 +78,10 @@ namespace ts {
7778 emitAssignment ( name , value , location ) ;
7879 return name ;
7980 }
81+
82+ function emitRestAssignment ( elements : ObjectLiteralElementLike [ ] , value : Expression , location : TextRange ) {
83+ emitAssignment ( createObjectLiteral ( elements ) , value , location ) ;
84+ }
8085 }
8186
8287 /**
@@ -89,14 +94,15 @@ namespace ts {
8994 export function flattenParameterDestructuring (
9095 node : ParameterDeclaration ,
9196 value : Expression ,
92- visitor ?: ( node : Node ) => VisitResult < Node > ) {
97+ visitor ?: ( node : Node ) => VisitResult < Node > ,
98+ transformRest ?: boolean ) {
9399 const declarations : VariableDeclaration [ ] = [ ] ;
94100
95- flattenDestructuring ( node , value , node , emitAssignment , emitTempVariableAssignment , visitor ) ;
101+ flattenDestructuring ( node , value , node , emitAssignment , emitTempVariableAssignment , emitRestAssignment , transformRest , visitor ) ;
96102
97103 return declarations ;
98104
99- function emitAssignment ( name : Identifier , value : Expression , location : TextRange ) {
105+ function emitAssignment ( name : Identifier | BindingPattern , value : Expression , location : TextRange ) {
100106 const declaration = createVariableDeclaration ( name , /*type*/ undefined , value , location ) ;
101107
102108 // NOTE: this completely disables source maps, but aligns with the behavior of
@@ -112,6 +118,10 @@ namespace ts {
112118 emitAssignment ( name , value , location ) ;
113119 return name ;
114120 }
121+
122+ function emitRestAssignment ( elements : BindingElement [ ] , value : Expression , location : TextRange ) {
123+ emitAssignment ( createObjectBindingPattern ( elements ) , value , location ) ;
124+ }
115125 }
116126
117127 /**
@@ -125,15 +135,16 @@ namespace ts {
125135 node : VariableDeclaration ,
126136 value ?: Expression ,
127137 visitor ?: ( node : Node ) => VisitResult < Node > ,
128- recordTempVariable ?: ( node : Identifier ) => void ) {
138+ recordTempVariable ?: ( node : Identifier ) => void ,
139+ transformRest ?: boolean ) {
129140 const declarations : VariableDeclaration [ ] = [ ] ;
130141
131142 let pendingAssignments : Expression [ ] ;
132- flattenDestructuring ( node , value , node , emitAssignment , emitTempVariableAssignment , visitor ) ;
143+ flattenDestructuring ( node , value , node , emitAssignment , emitTempVariableAssignment , emitRestAssignment , transformRest , visitor ) ;
133144
134145 return declarations ;
135146
136- function emitAssignment ( name : Identifier , value : Expression , location : TextRange , original : Node ) {
147+ function emitAssignment ( name : Identifier | BindingPattern , value : Expression , location : TextRange , original : Node ) {
137148 if ( pendingAssignments ) {
138149 pendingAssignments . push ( value ) ;
139150 value = inlineExpressions ( pendingAssignments ) ;
@@ -167,6 +178,10 @@ namespace ts {
167178 }
168179 return name ;
169180 }
181+
182+ function emitRestAssignment ( elements : BindingElement [ ] , value : Expression , location : TextRange , original : Node ) {
183+ emitAssignment ( createObjectBindingPattern ( elements ) , value , location , original ) ;
184+ }
170185 }
171186
172187 /**
@@ -186,15 +201,17 @@ namespace ts {
186201
187202 const pendingAssignments : Expression [ ] = [ ] ;
188203
189- flattenDestructuring ( node , /*value*/ undefined , node , emitAssignment , emitTempVariableAssignment , visitor ) ;
204+ flattenDestructuring ( node , /*value*/ undefined , node , emitAssignment , emitTempVariableAssignment , emitRestAssignment , /*transformRest*/ false , visitor ) ;
190205
191206 const expression = inlineExpressions ( pendingAssignments ) ;
192207 aggregateTransformFlags ( expression ) ;
193208 return expression ;
194209
195- function emitAssignment ( name : Identifier , value : Expression , location : TextRange , original : Node ) {
210+ function emitAssignment ( name : Identifier | ObjectLiteralExpression , value : Expression , location : TextRange , original : Node ) {
196211 const expression = createAssignmentCallback
197- ? createAssignmentCallback ( name , value , location )
212+ ? createAssignmentCallback ( name . kind === SyntaxKind . Identifier ? name : emitTempVariableAssignment ( name , location ) ,
213+ value ,
214+ location )
198215 : createAssignment ( name , value , location ) ;
199216
200217 emitPendingAssignment ( expression , original ) ;
@@ -206,6 +223,10 @@ namespace ts {
206223 return name ;
207224 }
208225
226+ function emitRestAssignment ( elements : ObjectLiteralElementLike [ ] , value : Expression , location : TextRange , original : Node ) {
227+ emitAssignment ( createObjectLiteral ( elements ) , value , location , original ) ;
228+ }
229+
209230 function emitPendingAssignment ( expression : Expression , original : Node ) {
210231 expression . original = original ;
211232
@@ -223,6 +244,8 @@ namespace ts {
223244 location : TextRange ,
224245 emitAssignment : ( name : Identifier , value : Expression , location : TextRange , original : Node ) => void ,
225246 emitTempVariableAssignment : ( value : Expression , location : TextRange ) => Identifier ,
247+ emitRestAssignment : ( elements : ( ObjectLiteralElementLike [ ] | BindingElement [ ] ) , value : Expression , location : TextRange , original : Node ) => void ,
248+ transformRest : boolean ,
226249 visitor ?: ( node : Node ) => VisitResult < Node > ) {
227250 if ( value && visitor ) {
228251 value = visitNode ( value , visitor , isExpression ) ;
@@ -284,14 +307,39 @@ namespace ts {
284307 value = ensureIdentifier ( value , /*reuseIdentifierExpressions*/ true , location , emitTempVariableAssignment ) ;
285308 }
286309
287- for ( const p of properties ) {
310+ let es2015 : ObjectLiteralElementLike [ ] = [ ] ;
311+ for ( let i = 0 ; i < properties . length ; i ++ ) {
312+ const p = properties [ i ] ;
288313 if ( p . kind === SyntaxKind . PropertyAssignment || p . kind === SyntaxKind . ShorthandPropertyAssignment ) {
289- const propName = < Identifier | LiteralExpression > ( < PropertyAssignment > p ) . name ;
290- const target = p . kind === SyntaxKind . ShorthandPropertyAssignment ? < ShorthandPropertyAssignment > p : ( < PropertyAssignment > p ) . initializer || propName ;
291- // Assignment for target = value.propName should highligh whole property, hence use p as source map node
292- emitDestructuringAssignment ( target , createDestructuringPropertyAccess ( value , propName ) , p ) ;
314+ if ( ! transformRest || p . transformFlags & TransformFlags . ContainsSpreadExpression ) {
315+ if ( es2015 . length ) {
316+ emitRestAssignment ( es2015 , value , location , target ) ;
317+ es2015 = [ ] ;
318+ }
319+ const propName = < Identifier | LiteralExpression > ( < PropertyAssignment > p ) . name ;
320+ const bindingTarget = p . kind === SyntaxKind . ShorthandPropertyAssignment ? < ShorthandPropertyAssignment > p : ( < PropertyAssignment > p ) . initializer || propName ;
321+ // Assignment for bindingTarget = value.propName should highlight whole property, hence use p as source map node
322+ emitDestructuringAssignment ( bindingTarget , createDestructuringPropertyAccess ( value , propName ) , p ) ;
323+ }
324+ else {
325+ es2015 . push ( p ) ;
326+ }
327+ }
328+ else if ( i === properties . length - 1 && p . kind === SyntaxKind . SpreadElementExpression ) {
329+ Debug . assert ( ( p as SpreadElementExpression ) . expression . kind === SyntaxKind . Identifier ) ;
330+ if ( es2015 . length ) {
331+ emitRestAssignment ( es2015 , value , location , target ) ;
332+ es2015 = [ ] ;
333+ }
334+ const propName = ( p as SpreadElementExpression ) . expression as Identifier ;
335+ const restCall = createRestCall ( value , target . properties , p => p . name , target ) ;
336+ emitDestructuringAssignment ( propName , restCall , p ) ;
293337 }
294338 }
339+ if ( es2015 . length ) {
340+ emitRestAssignment ( es2015 , value , location , target ) ;
341+ es2015 = [ ] ;
342+ }
295343 }
296344
297345 function emitArrayLiteralAssignment ( target : ArrayLiteralExpression , value : Expression , location : TextRange ) {
@@ -318,10 +366,31 @@ namespace ts {
318366 }
319367 }
320368
369+ /** Given value: o, propName: p, pattern: { a, b, ...p } from the original statement
370+ * `{ a, b, ...p } = o`, create `p = __rest(o, ["a", "b"]);`*/
371+ function createRestCall < T extends Node > ( value : Expression , elements : T [ ] , getPropertyName : ( element : T ) => PropertyName , location : TextRange ) : Expression {
372+ const propertyNames : LiteralExpression [ ] = [ ] ;
373+ for ( let i = 0 ; i < elements . length - 1 ; i ++ ) {
374+ if ( isOmittedExpression ( elements [ i ] ) ) {
375+ continue ;
376+ }
377+ const str = < StringLiteral > createSynthesizedNode ( SyntaxKind . StringLiteral ) ;
378+ str . pos = location . pos ;
379+ str . end = location . end ;
380+ str . text = getTextOfPropertyName ( getPropertyName ( elements [ i ] ) ) ;
381+ propertyNames . push ( str ) ;
382+ }
383+ const args = createSynthesizedNodeArray ( [ value , createArrayLiteral ( propertyNames , location ) ] ) ;
384+ return createCall ( createIdentifier ( "__rest" ) , undefined , args ) ;
385+ }
386+
321387 function emitBindingElement ( target : VariableDeclaration | ParameterDeclaration | BindingElement , value : Expression ) {
322388 // Any temporary assignments needed to emit target = value should point to target
323389 const initializer = visitor ? visitNode ( target . initializer , visitor , isExpression ) : target . initializer ;
324- if ( initializer ) {
390+ if ( transformRest ) {
391+ value = value || initializer ;
392+ }
393+ else if ( initializer ) {
325394 // Combine value and initializer
326395 value = value ? createDefaultValueCheck ( value , initializer , target ) : initializer ;
327396 }
@@ -331,39 +400,83 @@ namespace ts {
331400 }
332401
333402 const name = target . name ;
334- if ( isBindingPattern ( name ) ) {
335- const elements = name . elements ;
336- const numElements = elements . length ;
403+ if ( ! isBindingPattern ( name ) ) {
404+ emitAssignment ( name , value , target , target ) ;
405+ }
406+ else {
407+ const numElements = name . elements . length ;
337408 if ( numElements !== 1 ) {
338409 // For anything other than a single-element destructuring we need to generate a temporary
339410 // to ensure value is evaluated exactly once. Additionally, if we have zero elements
340411 // we need to emit *something* to ensure that in case a 'var' keyword was already emitted,
341412 // so in that case, we'll intentionally create that temporary.
342413 value = ensureIdentifier ( value , /*reuseIdentifierExpressions*/ numElements !== 0 , target , emitTempVariableAssignment ) ;
343414 }
344- for ( let i = 0 ; i < numElements ; i ++ ) {
345- const element = elements [ i ] ;
346- if ( isOmittedExpression ( element ) ) {
347- continue ;
348- }
349- else if ( name . kind === SyntaxKind . ObjectBindingPattern ) {
350- // Rewrite element to a declaration with an initializer that fetches property
351- const propName = element . propertyName || < Identifier > element . name ;
352- emitBindingElement ( element , createDestructuringPropertyAccess ( value , propName ) ) ;
415+ if ( name . kind === SyntaxKind . ArrayBindingPattern ) {
416+ emitArrayBindingElement ( name , value ) ;
417+ }
418+ else {
419+ emitObjectBindingElement ( target , value ) ;
420+ }
421+ }
422+ }
423+
424+ function emitArrayBindingElement ( name : BindingPattern , value : Expression ) {
425+ const elements = name . elements ;
426+ const numElements = elements . length ;
427+ for ( let i = 0 ; i < numElements ; i ++ ) {
428+ const element = elements [ i ] ;
429+ if ( isOmittedExpression ( element ) ) {
430+ continue ;
431+ }
432+ if ( ! element . dotDotDotToken ) {
433+ // Rewrite element to a declaration that accesses array element at index i
434+ emitBindingElement ( element , createElementAccess ( value , i ) ) ;
435+ }
436+ else if ( i === numElements - 1 ) {
437+ emitBindingElement ( element , createArraySlice ( value , i ) ) ;
438+ }
439+ }
440+ }
441+
442+ function emitObjectBindingElement ( target : VariableDeclaration | ParameterDeclaration | BindingElement , value : Expression ) {
443+ const name = target . name as BindingPattern ;
444+ const elements = name . elements ;
445+ const numElements = elements . length ;
446+ let es2015 : BindingElement [ ] = [ ] ;
447+ for ( let i = 0 ; i < numElements ; i ++ ) {
448+ const element = elements [ i ] ;
449+ if ( isOmittedExpression ( element ) ) {
450+ continue ;
451+ }
452+ if ( i === numElements - 1 && element . dotDotDotToken ) {
453+ if ( es2015 . length ) {
454+ emitRestAssignment ( es2015 , value , target , target ) ;
455+ es2015 = [ ] ;
353456 }
354- else {
355- if ( ! element . dotDotDotToken ) {
356- // Rewrite element to a declaration that accesses array element at index i
357- emitBindingElement ( element , createElementAccess ( value , i ) ) ;
358- }
359- else if ( i === numElements - 1 ) {
360- emitBindingElement ( element , createArraySlice ( value , i ) ) ;
361- }
457+ const restCall = createRestCall ( value ,
458+ name . elements ,
459+ element => ( element as BindingElement ) . propertyName || < Identifier > ( element as BindingElement ) . name ,
460+ name ) ;
461+ emitBindingElement ( element , restCall ) ;
462+ }
463+ else if ( ! transformRest || element . transformFlags & TransformFlags . ContainsSpreadExpression ) {
464+ if ( es2015 . length ) {
465+ emitRestAssignment ( es2015 , value , target , target ) ;
466+ es2015 = [ ] ;
362467 }
468+ // Rewrite element to a declaration with an initializer that fetches property
469+ const propName = element . propertyName || < Identifier > element . name ;
470+ emitBindingElement ( element , createDestructuringPropertyAccess ( value , propName ) ) ;
471+ }
472+ else {
473+ // do not emit until we have a complete bundle of ES2015 syntax
474+ es2015 . push ( element ) ;
363475 }
364476 }
365- else {
366- emitAssignment ( name , value , target , target ) ;
477+ if ( es2015 . length ) {
478+ emitRestAssignment ( es2015 , value , target , target ) ;
479+ es2015 = [ ] ;
367480 }
368481 }
369482
0 commit comments