@@ -2161,7 +2161,7 @@ module ts {
2161
2161
}
2162
2162
}
2163
2163
2164
- function emitParenthesized ( node : Node , parenthesized : boolean ) {
2164
+ function emitParenthesizedIf ( node : Node , parenthesized : boolean ) {
2165
2165
if ( parenthesized ) {
2166
2166
write ( "(" ) ;
2167
2167
}
@@ -2294,6 +2294,72 @@ module ts {
2294
2294
function getTemplateLiteralAsStringLiteral ( node : LiteralExpression ) : string {
2295
2295
return '"' + escapeString ( node . text ) + '"' ;
2296
2296
}
2297
+
2298
+ function emitDownlevelRawTemplateLiteral ( node : LiteralExpression ) {
2299
+ // Find original source text, since we need to emit the raw strings of the tagged template.
2300
+ // The raw strings contain the (escaped) strings of what the user wrote.
2301
+ // Examples: `\n` is converted to "\\n", a template string with a newline to "\n".
2302
+ var text = getSourceTextOfNodeFromSourceFile ( currentSourceFile , node ) ;
2303
+
2304
+ // text contains the original source, it will also contain quotes ("`"), dolar signs and braces ("${" and "}"),
2305
+ // thus we need to remove those characters.
2306
+ // First template piece starts with "`", others with " }"
2307
+ // Last template piece ends with "`", others with "${"
2308
+ var isLast = node . kind === SyntaxKind . NoSubstitutionTemplateLiteral || node . kind === SyntaxKind . TemplateTail ;
2309
+ text = text . substring ( 1 , text . length - ( isLast ? 1 : 2 ) ) ;
2310
+
2311
+ // Newline normalization:
2312
+ // ES6 Spec 11.8.6.1 - Static Semantics of TV's and TRV's
2313
+ // <CR><LF> and <CR> LineTerminatorSequences are normalized to <LF> for both TV and TRV.
2314
+ text = text . replace ( / \r \n ? / g, "\n" ) ;
2315
+ text = escapeString ( text ) ;
2316
+
2317
+ write ( '"' + text + '"' ) ;
2318
+ }
2319
+
2320
+ function emitDownlevelTaggedTemplateArray ( node : TaggedTemplateExpression , literalEmitter : ( literal : LiteralExpression ) => void ) {
2321
+ write ( "[" ) ;
2322
+ if ( node . template . kind === SyntaxKind . NoSubstitutionTemplateLiteral ) {
2323
+ literalEmitter ( < LiteralExpression > node . template ) ;
2324
+ }
2325
+ else {
2326
+ literalEmitter ( ( < TemplateExpression > node . template ) . head ) ;
2327
+ forEach ( ( < TemplateExpression > node . template ) . templateSpans , ( child ) => {
2328
+ write ( ", " ) ;
2329
+ literalEmitter ( child . literal ) ;
2330
+ } ) ;
2331
+ }
2332
+ write ( "]" ) ;
2333
+ }
2334
+
2335
+ function emitDownlevelTaggedTemplate ( node : TaggedTemplateExpression ) {
2336
+ var tempVariable = createAndRecordTempVariable ( node ) ;
2337
+ write ( "(" ) ;
2338
+ emit ( tempVariable ) ;
2339
+ write ( " = " ) ;
2340
+ emitDownlevelTaggedTemplateArray ( node , emit ) ;
2341
+ write ( ", " ) ;
2342
+
2343
+ emit ( tempVariable ) ;
2344
+ write ( ".raw = " ) ;
2345
+ emitDownlevelTaggedTemplateArray ( node , emitDownlevelRawTemplateLiteral ) ;
2346
+ write ( ", " ) ;
2347
+
2348
+ emitParenthesizedIf ( node . tag , needsParenthesisForPropertyAccessOrInvocation ( node . tag ) ) ;
2349
+ write ( "(" ) ;
2350
+ emit ( tempVariable ) ;
2351
+
2352
+ // Now we emit the expressions
2353
+ if ( node . template . kind === SyntaxKind . TemplateExpression ) {
2354
+ forEach ( ( < TemplateExpression > node . template ) . templateSpans , templateSpan => {
2355
+ write ( ", " ) ;
2356
+ var needsParens = templateSpan . expression . kind === SyntaxKind . BinaryExpression
2357
+ && ( < BinaryExpression > templateSpan . expression ) . operatorToken . kind === SyntaxKind . CommaToken ;
2358
+ emitParenthesizedIf ( templateSpan . expression , needsParens ) ;
2359
+ } ) ;
2360
+ }
2361
+ write ( "))" ) ;
2362
+ }
2297
2363
2298
2364
function emitTemplateExpression ( node : TemplateExpression ) : void {
2299
2365
// In ES6 mode and above, we can simply emit each portion of a template in order, but in
@@ -2338,7 +2404,8 @@ module ts {
2338
2404
write ( " + " ) ;
2339
2405
}
2340
2406
2341
- emitParenthesized ( templateSpan . expression , needsParens ) ;
2407
+ emitParenthesizedIf ( templateSpan . expression , needsParens ) ;
2408
+
2342
2409
// Only emit if the literal is non-empty.
2343
2410
// The binary '+' operator is left-associative, so the first string concatenation
2344
2411
// with the head will force the result up to this point to be a string.
@@ -2581,7 +2648,7 @@ module ts {
2581
2648
emit ( ( < SpreadElementExpression > node ) . expression ) ;
2582
2649
}
2583
2650
2584
- function needsParenthesisForPropertyAccess ( node : Expression ) {
2651
+ function needsParenthesisForPropertyAccessOrInvocation ( node : Expression ) {
2585
2652
switch ( node . kind ) {
2586
2653
case SyntaxKind . Identifier :
2587
2654
case SyntaxKind . ArrayLiteralExpression :
@@ -2611,7 +2678,7 @@ module ts {
2611
2678
var e = elements [ pos ] ;
2612
2679
if ( e . kind === SyntaxKind . SpreadElementExpression ) {
2613
2680
e = ( < SpreadElementExpression > e ) . expression ;
2614
- emitParenthesized ( e , /*parenthesized*/ group === 0 && needsParenthesisForPropertyAccess ( e ) ) ;
2681
+ emitParenthesizedIf ( e , /*parenthesized*/ group === 0 && needsParenthesisForPropertyAccessOrInvocation ( e ) ) ;
2615
2682
pos ++ ;
2616
2683
}
2617
2684
else {
@@ -3079,9 +3146,14 @@ module ts {
3079
3146
}
3080
3147
3081
3148
function emitTaggedTemplateExpression ( node : TaggedTemplateExpression ) : void {
3082
- emit ( node . tag ) ;
3083
- write ( " " ) ;
3084
- emit ( node . template ) ;
3149
+ if ( compilerOptions . target >= ScriptTarget . ES6 ) {
3150
+ emit ( node . tag ) ;
3151
+ write ( " " ) ;
3152
+ emit ( node . template ) ;
3153
+ }
3154
+ else {
3155
+ emitDownlevelTaggedTemplate ( node ) ;
3156
+ }
3085
3157
}
3086
3158
3087
3159
function emitParenExpression ( node : ParenthesizedExpression ) {
@@ -3182,71 +3254,21 @@ module ts {
3182
3254
3183
3255
write ( tokenToString ( node . operatorToken . kind ) ) ;
3184
3256
3185
- // We'd like to preserve newlines found in the original binary expression. i.e. if a user has:
3186
- //
3187
- // Foo() ||
3188
- // Bar();
3189
- //
3190
- // Then we'd like to emit it as such. It seems like we'd only need to check for a newline and
3191
- // then just indent and emit. However, that will lead to a problem with deeply nested code.
3192
- // i.e. if you have:
3193
- //
3194
- // Foo() ||
3195
- // Bar() ||
3196
- // Baz();
3197
- //
3198
- // Then we don't want to emit it as:
3199
- //
3200
- // Foo() ||
3201
- // Bar() ||
3202
- // Baz();
3203
- //
3204
- // So we only indent if the right side of the binary expression starts further in on the line
3205
- // versus the left.
3206
- var operatorEnd = getLineAndCharacterOfPosition ( currentSourceFile , node . operatorToken . end ) ;
3207
- var rightStart = getLineAndCharacterOfPosition ( currentSourceFile , skipTrivia ( currentSourceFile . text , node . right . pos ) ) ;
3208
-
3209
3257
// Check if the right expression is on a different line versus the operator itself. If so,
3210
3258
// we'll emit newline.
3211
- var onDifferentLine = operatorEnd . line !== rightStart . line ;
3212
- if ( onDifferentLine ) {
3213
- // Also, if the right expression starts further in on the line than the left, then we'll indent.
3214
- var exprStart = getLineAndCharacterOfPosition ( currentSourceFile , skipTrivia ( currentSourceFile . text , node . pos ) ) ;
3215
- var firstCharOfExpr = getFirstNonWhitespaceCharacterIndexOnLine ( exprStart . line ) ;
3216
- var shouldIndent = rightStart . character > firstCharOfExpr ;
3217
-
3218
- if ( shouldIndent ) {
3219
- increaseIndent ( ) ;
3220
- }
3221
-
3259
+ if ( ! nodeEndIsOnSameLineAsNodeStart ( node . operatorToken , node . right ) ) {
3260
+ increaseIndent ( ) ;
3222
3261
writeLine ( ) ;
3262
+ emit ( node . right ) ;
3263
+ decreaseIndent ( ) ;
3223
3264
}
3224
3265
else {
3225
3266
write ( " " ) ;
3226
- }
3227
-
3228
- emit ( node . right ) ;
3229
-
3230
- if ( shouldIndent ) {
3231
- decreaseIndent ( ) ;
3267
+ emit ( node . right ) ;
3232
3268
}
3233
3269
}
3234
3270
}
3235
3271
3236
- function getFirstNonWhitespaceCharacterIndexOnLine ( line : number ) : number {
3237
- var lineStart = getLineStarts ( currentSourceFile ) [ line ] ;
3238
- var text = currentSourceFile . text ;
3239
-
3240
- for ( var i = lineStart ; i < text . length ; i ++ ) {
3241
- var ch = text . charCodeAt ( i ) ;
3242
- if ( ! isWhiteSpace ( text . charCodeAt ( i ) ) || isLineBreak ( ch ) ) {
3243
- break ;
3244
- }
3245
- }
3246
-
3247
- return i - lineStart ;
3248
- }
3249
-
3250
3272
function emitConditionalExpression ( node : ConditionalExpression ) {
3251
3273
emit ( node . condition ) ;
3252
3274
write ( " ? " ) ;
@@ -3301,7 +3323,7 @@ module ts {
3301
3323
}
3302
3324
3303
3325
function emitExpressionStatement ( node : ExpressionStatement ) {
3304
- emitParenthesized ( node . expression , /*parenthesized*/ node . expression . kind === SyntaxKind . ArrowFunction ) ;
3326
+ emitParenthesizedIf ( node . expression , /*parenthesized*/ node . expression . kind === SyntaxKind . ArrowFunction ) ;
3305
3327
write ( ";" ) ;
3306
3328
}
3307
3329
@@ -4196,58 +4218,21 @@ module ts {
4196
4218
}
4197
4219
4198
4220
function emitBlockFunctionBody ( node : FunctionLikeDeclaration , body : Block ) {
4199
- // If the body has no statements, and we know there's no code that would cause any
4200
- // prologue to be emitted, then just do a simple emit if the empty block.
4201
- if ( body . statements . length === 0 && ! anyParameterHasBindingPatternOrInitializer ( node ) ) {
4202
- emitFunctionBodyWithNoStatements ( node , body ) ;
4203
- }
4204
- else {
4205
- emitFunctionBodyWithStatements ( node , body ) ;
4206
- }
4207
- }
4208
-
4209
- function anyParameterHasBindingPatternOrInitializer ( func : FunctionLikeDeclaration ) {
4210
- return forEach ( func . parameters , hasBindingPatternOrInitializer ) ;
4211
- }
4212
-
4213
- function hasBindingPatternOrInitializer ( parameter : ParameterDeclaration ) {
4214
- return parameter . initializer || isBindingPattern ( parameter . name ) ;
4215
- }
4216
-
4217
- function emitFunctionBodyWithNoStatements ( node : FunctionLikeDeclaration , body : Block ) {
4218
- var singleLine = isSingleLineEmptyBlock ( node . body ) ;
4219
-
4220
- write ( " {" ) ;
4221
- if ( singleLine ) {
4222
- write ( " " ) ;
4223
- }
4224
- else {
4225
- increaseIndent ( ) ;
4226
- writeLine ( ) ;
4227
- }
4228
-
4229
- emitLeadingCommentsOfPosition ( body . statements . end ) ;
4230
-
4231
- if ( ! singleLine ) {
4232
- decreaseIndent ( ) ;
4233
- }
4234
-
4235
- emitToken ( SyntaxKind . CloseBraceToken , body . statements . end ) ;
4236
- }
4237
-
4238
- function emitFunctionBodyWithStatements ( node : FunctionLikeDeclaration , body : Block ) {
4239
4221
write ( " {" ) ;
4240
4222
scopeEmitStart ( node ) ;
4241
4223
4242
- var outPos = writer . getTextPos ( ) ;
4224
+ var initialTextPos = writer . getTextPos ( ) ;
4243
4225
4244
4226
increaseIndent ( ) ;
4245
4227
emitDetachedComments ( body . statements ) ;
4228
+
4229
+ // Emit all the directive prologues (like "use strict"). These have to come before
4230
+ // any other preamble code we write (like parameter initializers).
4246
4231
var startIndex = emitDirectivePrologues ( body . statements , /*startWithNewLine*/ true ) ;
4247
4232
emitFunctionBodyPreamble ( node ) ;
4248
4233
decreaseIndent ( ) ;
4249
4234
4250
- var preambleEmitted = writer . getTextPos ( ) !== outPos ;
4235
+ var preambleEmitted = writer . getTextPos ( ) !== initialTextPos ;
4251
4236
4252
4237
if ( ! preambleEmitted && nodeEndIsOnSameLineAsNodeStart ( body , body ) ) {
4253
4238
for ( var i = 0 , n = body . statements . length ; i < n ; i ++ ) {
0 commit comments