From 87ae723b527bd8958cf45b47b23f42b28cc48b3d Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Thu, 8 Sep 2016 09:50:49 -0700 Subject: [PATCH] For JSX text, construct a single literal node `"foo bar"` instead of `"foo" + " " + "bar"`. --- src/compiler/transformers/jsx.ts | 55 +++++++------------ .../reference/tsxReactEmitWhitespace.js | 6 +- .../reference/tsxReactEmitWhitespace.symbols | 2 +- .../reference/tsxReactEmitWhitespace.types | 2 +- .../jsx/tsxReactEmitWhitespace.tsx | 2 +- 5 files changed, 26 insertions(+), 41 deletions(-) diff --git a/src/compiler/transformers/jsx.ts b/src/compiler/transformers/jsx.ts index 9e6aa507cc..354c1bcad7 100644 --- a/src/compiler/transformers/jsx.ts +++ b/src/compiler/transformers/jsx.ts @@ -151,28 +151,29 @@ namespace ts { } } - function visitJsxText(node: JsxText) { - const text = getTextOfNode(node, /*includeTrivia*/ true); - let parts: Expression[]; + function visitJsxText(node: JsxText): StringLiteral | undefined { + const fixed = fixupWhitespaceAndDecodeEntities(getTextOfNode(node, /*includeTrivia*/ true)); + return fixed !== undefined && createLiteral(fixed); + } + + /** + * JSX trims whitespace at the end and beginning of lines, except that the + * start/end of a tag is considered a start/end of a line only if that line is + * on the same line as the closing tag. See examples in + * tests/cases/conformance/jsx/tsxReactEmitWhitespace.tsx + * See also https://www.w3.org/TR/html4/struct/text.html#h-9.1 and https://www.w3.org/TR/CSS2/text.html#white-space-model + */ + function fixupWhitespaceAndDecodeEntities(text: string): string | undefined { + let acc: string | undefined; let firstNonWhitespace = 0; let lastNonWhitespace = -1; - // JSX trims whitespace at the end and beginning of lines, except that the - // start/end of a tag is considered a start/end of a line only if that line is - // on the same line as the closing tag. See examples in - // tests/cases/conformance/jsx/tsxReactEmitWhitespace.tsx for (let i = 0; i < text.length; i++) { const c = text.charCodeAt(i); if (isLineBreak(c)) { if (firstNonWhitespace !== -1 && (lastNonWhitespace - firstNonWhitespace + 1 > 0)) { - const part = text.substr(firstNonWhitespace, lastNonWhitespace - firstNonWhitespace + 1); - if (!parts) { - parts = []; - } - - // We do not escape the string here as that is handled by the printer - // when it emits the literal. We do, however, need to decode JSX entities. - parts.push(createLiteral(decodeEntities(part))); + const part = decodeEntities(text.substr(firstNonWhitespace, lastNonWhitespace - firstNonWhitespace + 1)); + acc = acc === undefined ? part : acc + " " + part; } firstNonWhitespace = -1; @@ -186,28 +187,12 @@ namespace ts { } if (firstNonWhitespace !== -1) { - const part = text.substr(firstNonWhitespace); - if (!parts) { - parts = []; - } - - // We do not escape the string here as that is handled by the printer - // when it emits the literal. We do, however, need to decode JSX entities. - parts.push(createLiteral(decodeEntities(part))); + const lastPart = decodeEntities(text.substr(firstNonWhitespace)); + return acc ? acc + lastPart : lastPart; } - - if (parts) { - return reduceLeft(parts, aggregateJsxTextParts); + else { + return acc; } - - return undefined; - } - - /** - * Aggregates two expressions by interpolating them with a whitespace literal. - */ - function aggregateJsxTextParts(left: Expression, right: Expression) { - return createAdd(createAdd(left, createLiteral(" ")), right); } /** diff --git a/tests/baselines/reference/tsxReactEmitWhitespace.js b/tests/baselines/reference/tsxReactEmitWhitespace.js index dd69091569..1588b53d8f 100644 --- a/tests/baselines/reference/tsxReactEmitWhitespace.js +++ b/tests/baselines/reference/tsxReactEmitWhitespace.js @@ -41,7 +41,7 @@ var p = 0;
; -// Emit "foo" + ' ' + "bar" +// Emit "foo bar"
foo @@ -75,5 +75,5 @@ React.createElement("div", null, " 3 "); React.createElement("div", null, "3"); // Emit no args React.createElement("div", null); -// Emit "foo" + ' ' + "bar" -React.createElement("div", null, "foo" + " " + "bar"); +// Emit "foo bar" +React.createElement("div", null, "foo bar"); diff --git a/tests/baselines/reference/tsxReactEmitWhitespace.symbols b/tests/baselines/reference/tsxReactEmitWhitespace.symbols index a0d8266faa..4639e828b5 100644 --- a/tests/baselines/reference/tsxReactEmitWhitespace.symbols +++ b/tests/baselines/reference/tsxReactEmitWhitespace.symbols @@ -79,7 +79,7 @@ var p = 0;
; >div : Symbol(JSX.IntrinsicElements, Decl(file.tsx, 1, 22)) -// Emit "foo" + ' ' + "bar" +// Emit "foo bar"
>div : Symbol(JSX.IntrinsicElements, Decl(file.tsx, 1, 22)) diff --git a/tests/baselines/reference/tsxReactEmitWhitespace.types b/tests/baselines/reference/tsxReactEmitWhitespace.types index 824aa5cfda..ce6911f987 100644 --- a/tests/baselines/reference/tsxReactEmitWhitespace.types +++ b/tests/baselines/reference/tsxReactEmitWhitespace.types @@ -88,7 +88,7 @@ var p = 0;
; >div : any -// Emit "foo" + ' ' + "bar" +// Emit "foo bar"
>
foo bar
: JSX.Element >div : any diff --git a/tests/cases/conformance/jsx/tsxReactEmitWhitespace.tsx b/tests/cases/conformance/jsx/tsxReactEmitWhitespace.tsx index 34fd158eab..9977803e39 100644 --- a/tests/cases/conformance/jsx/tsxReactEmitWhitespace.tsx +++ b/tests/cases/conformance/jsx/tsxReactEmitWhitespace.tsx @@ -42,7 +42,7 @@ var p = 0;
; -// Emit "foo" + ' ' + "bar" +// Emit "foo bar"
foo