From b442d14e440cd3db7a797016e9b11de2b3f12cb3 Mon Sep 17 00:00:00 2001 From: Arnavion Date: Tue, 6 Jan 2015 15:28:06 -0800 Subject: [PATCH] Don't emit an empty template head literal if there's a template span with a non-empty literal. Fixes #1570 --- src/compiler/emitter.ts | 43 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 39 insertions(+), 4 deletions(-) diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index f3e45a85b6a..ba32e46065e 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -2068,9 +2068,15 @@ module ts { write("("); } - emitLiteral(node.head); + var headEmitted = false; + if (shouldEmitTemplateHead()) { + emitLiteral(node.head); + headEmitted = true; + } + + for (var i = 0; i < node.templateSpans.length; i++) { + var templateSpan = node.templateSpans[i]; - forEach(node.templateSpans, templateSpan => { // Check if the expression has operands and binds its operands less closely than binary '+'. // If it does, we need to wrap the expression in parentheses. Otherwise, something like // `abc${ 1 << 2 }` @@ -2082,7 +2088,14 @@ module ts { // "abc" + (1 << 2) + "" var needsParens = templateSpan.expression.kind !== SyntaxKind.ParenthesizedExpression && comparePrecedenceToBinaryPlus(templateSpan.expression) !== Comparison.GreaterThan; - write(" + "); + + if (i > 0 || headEmitted) { + // If this is the first span and the head was not emitted, then this templateSpan's + // expression will be the first to be emitted. Don't emit the preceding ' + ' in that + // case. + write(" + "); + } + emitParenthesized(templateSpan.expression, needsParens); // Only emit if the literal is non-empty. // The binary '+' operator is left-associative, so the first string concatenation @@ -2092,12 +2105,34 @@ module ts { write(" + ") emitLiteral(templateSpan.literal); } - }); + } if (emitOuterParens) { write(")"); } + function shouldEmitTemplateHead() { + // If this expression has an empty head literal and the first template span has a non-empty + // literal, then emitting the empty head literal is not necessary. + // `${ foo } and ${ bar }` + // can be emitted as + // foo + " and " + bar + // This is because it is only required that one of the first two operands in the emit + // output must be a string literal, so that the other operand and all following operands + // are forced into strings. + // + // If the first template span has an empty literal, then the head must still be emitted. + // `${ foo }${ bar }` + // must still be emitted as + // "" + foo + bar + + // There is always atleast one templateSpan in this code path, since + // NoSubstitutionTemplateLiterals are directly emitted via emitLiteral() + Debug.assert(node.templateSpans.length !== 0); + + return node.head.text.length !== 0 || node.templateSpans[0].literal.text.length === 0; + } + function templateNeedsParens(template: TemplateExpression, parent: Expression) { switch (parent.kind) { case SyntaxKind.CallExpression: