Skip to content

Commit 3ea81e6

Browse files
authored
Ensure whitespace jsx elements are not counted when determining if a jsx child is the only child (#40839)
* Ensure whitespace jsx elements are not counted when determining if a jsx child is the only child * Use filtered children count for deciding constructor used * Accept updated baselines post-merge
1 parent 5fbe980 commit 3ea81e6

8 files changed

+378
-11
lines changed

src/compiler/transformers/jsx.ts

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,12 @@ namespace ts {
2626
return currentFileState.filenameDeclaration.name;
2727
}
2828

29-
function getJsxFactoryCalleePrimitive(children: readonly JsxChild[] | undefined): "jsx" | "jsxs" | "jsxDEV" {
30-
return compilerOptions.jsx === JsxEmit.ReactJSXDev ? "jsxDEV" : children && children.length > 1 ? "jsxs" : "jsx";
29+
function getJsxFactoryCalleePrimitive(childrenLength: number): "jsx" | "jsxs" | "jsxDEV" {
30+
return compilerOptions.jsx === JsxEmit.ReactJSXDev ? "jsxDEV" : childrenLength > 1 ? "jsxs" : "jsx";
3131
}
3232

33-
function getJsxFactoryCallee(children: readonly JsxChild[] | undefined) {
34-
const type = getJsxFactoryCalleePrimitive(children);
33+
function getJsxFactoryCallee(childrenLength: number) {
34+
const type = getJsxFactoryCalleePrimitive(childrenLength);
3535
return getImplicitImportForName(type);
3636
}
3737

@@ -191,8 +191,9 @@ namespace ts {
191191
}
192192

193193
function convertJsxChildrenToChildrenPropObject(children: readonly JsxChild[]) {
194-
if (children.length === 1) {
195-
const result = transformJsxChildToExpression(children[0]);
194+
const nonWhitespaceChildren = filter(children, c => !isJsxText(c) || !c.containsOnlyTriviaWhiteSpaces);
195+
if (length(nonWhitespaceChildren) === 1) {
196+
const result = transformJsxChildToExpression(nonWhitespaceChildren[0]);
196197
return result && factory.createObjectLiteralExpression([
197198
factory.createPropertyAssignment("children", result)
198199
]);
@@ -243,16 +244,16 @@ namespace ts {
243244
objectProperties = singleOrUndefined(segments) || emitHelpers().createAssignHelper(segments);
244245
}
245246

246-
return visitJsxOpeningLikeElementOrFragmentJSX(tagName, objectProperties, keyAttr, children, isChild, location);
247+
return visitJsxOpeningLikeElementOrFragmentJSX(tagName, objectProperties, keyAttr, length(filter(children, c => !isJsxText(c) || !c.containsOnlyTriviaWhiteSpaces)), isChild, location);
247248
}
248249

249-
function visitJsxOpeningLikeElementOrFragmentJSX(tagName: Expression, objectProperties: Expression, keyAttr: JsxAttribute | undefined, children: readonly JsxChild[] | undefined, isChild: boolean, location: TextRange) {
250+
function visitJsxOpeningLikeElementOrFragmentJSX(tagName: Expression, objectProperties: Expression, keyAttr: JsxAttribute | undefined, childrenLength: number, isChild: boolean, location: TextRange) {
250251
const args: Expression[] = [tagName, objectProperties, !keyAttr ? factory.createVoidZero() : transformJsxAttributeInitializer(keyAttr.initializer)];
251252
if (compilerOptions.jsx === JsxEmit.ReactJSXDev) {
252253
const originalFile = getOriginalNode(currentSourceFile);
253254
if (originalFile && isSourceFile(originalFile)) {
254255
// isStaticChildren development flag
255-
args.push(children && children.length > 1 ? factory.createTrue() : factory.createFalse());
256+
args.push(childrenLength > 1 ? factory.createTrue() : factory.createFalse());
256257
// __source development flag
257258
const lineCol = getLineAndCharacterOfPosition(originalFile, location.pos);
258259
args.push(factory.createObjectLiteralExpression([
@@ -264,7 +265,7 @@ namespace ts {
264265
args.push(factory.createThis());
265266
}
266267
}
267-
const element = setTextRange(factory.createCallExpression(getJsxFactoryCallee(children), /*typeArguments*/ undefined, args), location);
268+
const element = setTextRange(factory.createCallExpression(getJsxFactoryCallee(childrenLength), /*typeArguments*/ undefined, args), location);
268269

269270
if (isChild) {
270271
startOnNewLine(element);
@@ -335,7 +336,7 @@ namespace ts {
335336
getImplicitJsxFragmentReference(),
336337
childrenProps || factory.createObjectLiteralExpression([]),
337338
/*keyAttr*/ undefined,
338-
children,
339+
length(filter(children, c => !isJsxText(c) || !c.containsOnlyTriviaWhiteSpaces)),
339340
isChild,
340341
location
341342
);
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
//// [jsxJsxsCjsTransformNestedSelfClosingChild.tsx]
2+
/// <reference path="/.lib/react16.d.ts" />
3+
import type * as React from 'react';
4+
5+
console.log(
6+
<div>
7+
<div />
8+
</div>
9+
)
10+
11+
console.log(
12+
<div>
13+
<div />
14+
<div />
15+
</div>
16+
)
17+
18+
console.log(
19+
<div>
20+
{[1, 2].map(i => <div key={i}>{i}</div>)}
21+
</div>
22+
)
23+
24+
//// [jsxJsxsCjsTransformNestedSelfClosingChild.js]
25+
"use strict";
26+
exports.__esModule = true;
27+
var jsx_runtime_js_1 = require("react/jsx-runtime.js");
28+
console.log(jsx_runtime_js_1.jsx("div", { children: jsx_runtime_js_1.jsx("div", {}, void 0) }, void 0));
29+
console.log(jsx_runtime_js_1.jsxs("div", { children: [jsx_runtime_js_1.jsx("div", {}, void 0),
30+
jsx_runtime_js_1.jsx("div", {}, void 0)] }, void 0));
31+
console.log(jsx_runtime_js_1.jsx("div", { children: [1, 2].map(function (i) { return jsx_runtime_js_1.jsx("div", { children: i }, i); }) }, void 0));
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
=== tests/cases/conformance/jsx/jsxs/jsxJsxsCjsTransformNestedSelfClosingChild.tsx ===
2+
/// <reference path="react16.d.ts" />
3+
import type * as React from 'react';
4+
>React : Symbol(React, Decl(jsxJsxsCjsTransformNestedSelfClosingChild.tsx, 1, 11))
5+
6+
console.log(
7+
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
8+
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
9+
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
10+
11+
<div>
12+
>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2420, 114))
13+
14+
<div />
15+
>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2420, 114))
16+
17+
</div>
18+
>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2420, 114))
19+
20+
)
21+
22+
console.log(
23+
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
24+
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
25+
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
26+
27+
<div>
28+
>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2420, 114))
29+
30+
<div />
31+
>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2420, 114))
32+
33+
<div />
34+
>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2420, 114))
35+
36+
</div>
37+
>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2420, 114))
38+
39+
)
40+
41+
console.log(
42+
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
43+
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
44+
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
45+
46+
<div>
47+
>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2420, 114))
48+
49+
{[1, 2].map(i => <div key={i}>{i}</div>)}
50+
>[1, 2].map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --))
51+
>map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --))
52+
>i : Symbol(i, Decl(jsxJsxsCjsTransformNestedSelfClosingChild.tsx, 18, 16))
53+
>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2420, 114))
54+
>key : Symbol(key, Decl(jsxJsxsCjsTransformNestedSelfClosingChild.tsx, 18, 25))
55+
>i : Symbol(i, Decl(jsxJsxsCjsTransformNestedSelfClosingChild.tsx, 18, 16))
56+
>i : Symbol(i, Decl(jsxJsxsCjsTransformNestedSelfClosingChild.tsx, 18, 16))
57+
>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2420, 114))
58+
59+
</div>
60+
>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2420, 114))
61+
62+
)
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
=== tests/cases/conformance/jsx/jsxs/jsxJsxsCjsTransformNestedSelfClosingChild.tsx ===
2+
/// <reference path="react16.d.ts" />
3+
import type * as React from 'react';
4+
>React : typeof React
5+
6+
console.log(
7+
>console.log( <div> <div /> </div>) : void
8+
>console.log : (...data: any[]) => void
9+
>console : Console
10+
>log : (...data: any[]) => void
11+
12+
<div>
13+
><div> <div /> </div> : JSX.Element
14+
>div : any
15+
16+
<div />
17+
><div /> : JSX.Element
18+
>div : any
19+
20+
</div>
21+
>div : any
22+
23+
)
24+
25+
console.log(
26+
>console.log( <div> <div /> <div /> </div>) : void
27+
>console.log : (...data: any[]) => void
28+
>console : Console
29+
>log : (...data: any[]) => void
30+
31+
<div>
32+
><div> <div /> <div /> </div> : JSX.Element
33+
>div : any
34+
35+
<div />
36+
><div /> : JSX.Element
37+
>div : any
38+
39+
<div />
40+
><div /> : JSX.Element
41+
>div : any
42+
43+
</div>
44+
>div : any
45+
46+
)
47+
48+
console.log(
49+
>console.log( <div> {[1, 2].map(i => <div key={i}>{i}</div>)} </div>) : void
50+
>console.log : (...data: any[]) => void
51+
>console : Console
52+
>log : (...data: any[]) => void
53+
54+
<div>
55+
><div> {[1, 2].map(i => <div key={i}>{i}</div>)} </div> : JSX.Element
56+
>div : any
57+
58+
{[1, 2].map(i => <div key={i}>{i}</div>)}
59+
>[1, 2].map(i => <div key={i}>{i}</div>) : JSX.Element[]
60+
>[1, 2].map : <U>(callbackfn: (value: number, index: number, array: number[]) => U, thisArg?: any) => U[]
61+
>[1, 2] : number[]
62+
>1 : 1
63+
>2 : 2
64+
>map : <U>(callbackfn: (value: number, index: number, array: number[]) => U, thisArg?: any) => U[]
65+
>i => <div key={i}>{i}</div> : (i: number) => JSX.Element
66+
>i : number
67+
><div key={i}>{i}</div> : JSX.Element
68+
>div : any
69+
>key : number
70+
>i : number
71+
>i : number
72+
>div : any
73+
74+
</div>
75+
>div : any
76+
77+
)
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
//// [jsxJsxsCjsTransformNestedSelfClosingChild.tsx]
2+
/// <reference path="/.lib/react16.d.ts" />
3+
import type * as React from 'react';
4+
5+
console.log(
6+
<div>
7+
<div />
8+
</div>
9+
)
10+
11+
console.log(
12+
<div>
13+
<div />
14+
<div />
15+
</div>
16+
)
17+
18+
console.log(
19+
<div>
20+
{[1, 2].map(i => <div key={i}>{i}</div>)}
21+
</div>
22+
)
23+
24+
//// [jsxJsxsCjsTransformNestedSelfClosingChild.js]
25+
"use strict";
26+
var _this = this;
27+
exports.__esModule = true;
28+
var jsx_dev_runtime_js_1 = require("react/jsx-dev-runtime.js");
29+
var _jsxFileName = "tests/cases/conformance/jsx/jsxs/jsxJsxsCjsTransformNestedSelfClosingChild.tsx";
30+
console.log(jsx_dev_runtime_js_1.jsxDEV("div", { children: jsx_dev_runtime_js_1.jsxDEV("div", {}, void 0, false, { fileName: _jsxFileName, lineNumber: 6, columnNumber: 5 }, this) }, void 0, false, { fileName: _jsxFileName, lineNumber: 4, columnNumber: 13 }, this));
31+
console.log(jsx_dev_runtime_js_1.jsxDEV("div", { children: [jsx_dev_runtime_js_1.jsxDEV("div", {}, void 0, false, { fileName: _jsxFileName, lineNumber: 12, columnNumber: 5 }, this),
32+
jsx_dev_runtime_js_1.jsxDEV("div", {}, void 0, false, { fileName: _jsxFileName, lineNumber: 13, columnNumber: 5 }, this)] }, void 0, true, { fileName: _jsxFileName, lineNumber: 10, columnNumber: 13 }, this));
33+
console.log(jsx_dev_runtime_js_1.jsxDEV("div", { children: [1, 2].map(function (i) { return jsx_dev_runtime_js_1.jsxDEV("div", { children: i }, i, false, { fileName: _jsxFileName, lineNumber: 19, columnNumber: 21 }, _this); }) }, void 0, false, { fileName: _jsxFileName, lineNumber: 17, columnNumber: 13 }, this));
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
=== tests/cases/conformance/jsx/jsxs/jsxJsxsCjsTransformNestedSelfClosingChild.tsx ===
2+
/// <reference path="react16.d.ts" />
3+
import type * as React from 'react';
4+
>React : Symbol(React, Decl(jsxJsxsCjsTransformNestedSelfClosingChild.tsx, 1, 11))
5+
6+
console.log(
7+
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
8+
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
9+
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
10+
11+
<div>
12+
>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2420, 114))
13+
14+
<div />
15+
>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2420, 114))
16+
17+
</div>
18+
>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2420, 114))
19+
20+
)
21+
22+
console.log(
23+
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
24+
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
25+
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
26+
27+
<div>
28+
>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2420, 114))
29+
30+
<div />
31+
>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2420, 114))
32+
33+
<div />
34+
>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2420, 114))
35+
36+
</div>
37+
>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2420, 114))
38+
39+
)
40+
41+
console.log(
42+
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
43+
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
44+
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
45+
46+
<div>
47+
>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2420, 114))
48+
49+
{[1, 2].map(i => <div key={i}>{i}</div>)}
50+
>[1, 2].map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --))
51+
>map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --))
52+
>i : Symbol(i, Decl(jsxJsxsCjsTransformNestedSelfClosingChild.tsx, 18, 16))
53+
>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2420, 114))
54+
>key : Symbol(key, Decl(jsxJsxsCjsTransformNestedSelfClosingChild.tsx, 18, 25))
55+
>i : Symbol(i, Decl(jsxJsxsCjsTransformNestedSelfClosingChild.tsx, 18, 16))
56+
>i : Symbol(i, Decl(jsxJsxsCjsTransformNestedSelfClosingChild.tsx, 18, 16))
57+
>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2420, 114))
58+
59+
</div>
60+
>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2420, 114))
61+
62+
)

0 commit comments

Comments
 (0)