Skip to content

Commit 87ae723

Browse files
author
Andy Hanson
committed
For JSX text, construct a single literal node "foo bar" instead of "foo" + " " + "bar".
1 parent 7f84953 commit 87ae723

File tree

5 files changed

+26
-41
lines changed

5 files changed

+26
-41
lines changed

src/compiler/transformers/jsx.ts

Lines changed: 20 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -151,28 +151,29 @@ namespace ts {
151151
}
152152
}
153153

154-
function visitJsxText(node: JsxText) {
155-
const text = getTextOfNode(node, /*includeTrivia*/ true);
156-
let parts: Expression[];
154+
function visitJsxText(node: JsxText): StringLiteral | undefined {
155+
const fixed = fixupWhitespaceAndDecodeEntities(getTextOfNode(node, /*includeTrivia*/ true));
156+
return fixed !== undefined && createLiteral(fixed);
157+
}
158+
159+
/**
160+
* JSX trims whitespace at the end and beginning of lines, except that the
161+
* start/end of a tag is considered a start/end of a line only if that line is
162+
* on the same line as the closing tag. See examples in
163+
* tests/cases/conformance/jsx/tsxReactEmitWhitespace.tsx
164+
* 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
165+
*/
166+
function fixupWhitespaceAndDecodeEntities(text: string): string | undefined {
167+
let acc: string | undefined;
157168
let firstNonWhitespace = 0;
158169
let lastNonWhitespace = -1;
159170

160-
// JSX trims whitespace at the end and beginning of lines, except that the
161-
// start/end of a tag is considered a start/end of a line only if that line is
162-
// on the same line as the closing tag. See examples in
163-
// tests/cases/conformance/jsx/tsxReactEmitWhitespace.tsx
164171
for (let i = 0; i < text.length; i++) {
165172
const c = text.charCodeAt(i);
166173
if (isLineBreak(c)) {
167174
if (firstNonWhitespace !== -1 && (lastNonWhitespace - firstNonWhitespace + 1 > 0)) {
168-
const part = text.substr(firstNonWhitespace, lastNonWhitespace - firstNonWhitespace + 1);
169-
if (!parts) {
170-
parts = [];
171-
}
172-
173-
// We do not escape the string here as that is handled by the printer
174-
// when it emits the literal. We do, however, need to decode JSX entities.
175-
parts.push(createLiteral(decodeEntities(part)));
175+
const part = decodeEntities(text.substr(firstNonWhitespace, lastNonWhitespace - firstNonWhitespace + 1));
176+
acc = acc === undefined ? part : acc + " " + part;
176177
}
177178

178179
firstNonWhitespace = -1;
@@ -186,28 +187,12 @@ namespace ts {
186187
}
187188

188189
if (firstNonWhitespace !== -1) {
189-
const part = text.substr(firstNonWhitespace);
190-
if (!parts) {
191-
parts = [];
192-
}
193-
194-
// We do not escape the string here as that is handled by the printer
195-
// when it emits the literal. We do, however, need to decode JSX entities.
196-
parts.push(createLiteral(decodeEntities(part)));
190+
const lastPart = decodeEntities(text.substr(firstNonWhitespace));
191+
return acc ? acc + lastPart : lastPart;
197192
}
198-
199-
if (parts) {
200-
return reduceLeft(parts, aggregateJsxTextParts);
193+
else {
194+
return acc;
201195
}
202-
203-
return undefined;
204-
}
205-
206-
/**
207-
* Aggregates two expressions by interpolating them with a whitespace literal.
208-
*/
209-
function aggregateJsxTextParts(left: Expression, right: Expression) {
210-
return createAdd(createAdd(left, createLiteral(" ")), right);
211196
}
212197

213198
/**

tests/baselines/reference/tsxReactEmitWhitespace.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ var p = 0;
4141
<div>
4242
</div>;
4343

44-
// Emit "foo" + ' ' + "bar"
44+
// Emit "foo bar"
4545
<div>
4646

4747
foo
@@ -75,5 +75,5 @@ React.createElement("div", null, " 3 ");
7575
React.createElement("div", null, "3");
7676
// Emit no args
7777
React.createElement("div", null);
78-
// Emit "foo" + ' ' + "bar"
79-
React.createElement("div", null, "foo" + " " + "bar");
78+
// Emit "foo bar"
79+
React.createElement("div", null, "foo bar");

tests/baselines/reference/tsxReactEmitWhitespace.symbols

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ var p = 0;
7979
</div>;
8080
>div : Symbol(JSX.IntrinsicElements, Decl(file.tsx, 1, 22))
8181

82-
// Emit "foo" + ' ' + "bar"
82+
// Emit "foo bar"
8383
<div>
8484
>div : Symbol(JSX.IntrinsicElements, Decl(file.tsx, 1, 22))
8585

tests/baselines/reference/tsxReactEmitWhitespace.types

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ var p = 0;
8888
</div>;
8989
>div : any
9090

91-
// Emit "foo" + ' ' + "bar"
91+
// Emit "foo bar"
9292
<div>
9393
><div> foo bar </div> : JSX.Element
9494
>div : any

tests/cases/conformance/jsx/tsxReactEmitWhitespace.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ var p = 0;
4242
<div>
4343
</div>;
4444

45-
// Emit "foo" + ' ' + "bar"
45+
// Emit "foo bar"
4646
<div>
4747

4848
foo

0 commit comments

Comments
 (0)