From 3ea81e652aec2f76b9ec454e7342cb7a169284b9 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Thu, 1 Oct 2020 14:36:44 -0700 Subject: [PATCH] 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 --- src/compiler/transformers/jsx.ts | 23 +++--- ...rmNestedSelfClosingChild(jsx=react-jsx).js | 31 ++++++++ ...tedSelfClosingChild(jsx=react-jsx).symbols | 62 +++++++++++++++ ...estedSelfClosingChild(jsx=react-jsx).types | 77 +++++++++++++++++++ ...estedSelfClosingChild(jsx=react-jsxdev).js | 33 ++++++++ ...SelfClosingChild(jsx=react-jsxdev).symbols | 62 +++++++++++++++ ...edSelfClosingChild(jsx=react-jsxdev).types | 77 +++++++++++++++++++ ...JsxsCjsTransformNestedSelfClosingChild.tsx | 24 ++++++ 8 files changed, 378 insertions(+), 11 deletions(-) create mode 100644 tests/baselines/reference/jsxJsxsCjsTransformNestedSelfClosingChild(jsx=react-jsx).js create mode 100644 tests/baselines/reference/jsxJsxsCjsTransformNestedSelfClosingChild(jsx=react-jsx).symbols create mode 100644 tests/baselines/reference/jsxJsxsCjsTransformNestedSelfClosingChild(jsx=react-jsx).types create mode 100644 tests/baselines/reference/jsxJsxsCjsTransformNestedSelfClosingChild(jsx=react-jsxdev).js create mode 100644 tests/baselines/reference/jsxJsxsCjsTransformNestedSelfClosingChild(jsx=react-jsxdev).symbols create mode 100644 tests/baselines/reference/jsxJsxsCjsTransformNestedSelfClosingChild(jsx=react-jsxdev).types create mode 100644 tests/cases/conformance/jsx/jsxs/jsxJsxsCjsTransformNestedSelfClosingChild.tsx diff --git a/src/compiler/transformers/jsx.ts b/src/compiler/transformers/jsx.ts index 9d4b1296620..905bfad32e0 100644 --- a/src/compiler/transformers/jsx.ts +++ b/src/compiler/transformers/jsx.ts @@ -26,12 +26,12 @@ namespace ts { return currentFileState.filenameDeclaration.name; } - function getJsxFactoryCalleePrimitive(children: readonly JsxChild[] | undefined): "jsx" | "jsxs" | "jsxDEV" { - return compilerOptions.jsx === JsxEmit.ReactJSXDev ? "jsxDEV" : children && children.length > 1 ? "jsxs" : "jsx"; + function getJsxFactoryCalleePrimitive(childrenLength: number): "jsx" | "jsxs" | "jsxDEV" { + return compilerOptions.jsx === JsxEmit.ReactJSXDev ? "jsxDEV" : childrenLength > 1 ? "jsxs" : "jsx"; } - function getJsxFactoryCallee(children: readonly JsxChild[] | undefined) { - const type = getJsxFactoryCalleePrimitive(children); + function getJsxFactoryCallee(childrenLength: number) { + const type = getJsxFactoryCalleePrimitive(childrenLength); return getImplicitImportForName(type); } @@ -191,8 +191,9 @@ namespace ts { } function convertJsxChildrenToChildrenPropObject(children: readonly JsxChild[]) { - if (children.length === 1) { - const result = transformJsxChildToExpression(children[0]); + const nonWhitespaceChildren = filter(children, c => !isJsxText(c) || !c.containsOnlyTriviaWhiteSpaces); + if (length(nonWhitespaceChildren) === 1) { + const result = transformJsxChildToExpression(nonWhitespaceChildren[0]); return result && factory.createObjectLiteralExpression([ factory.createPropertyAssignment("children", result) ]); @@ -243,16 +244,16 @@ namespace ts { objectProperties = singleOrUndefined(segments) || emitHelpers().createAssignHelper(segments); } - return visitJsxOpeningLikeElementOrFragmentJSX(tagName, objectProperties, keyAttr, children, isChild, location); + return visitJsxOpeningLikeElementOrFragmentJSX(tagName, objectProperties, keyAttr, length(filter(children, c => !isJsxText(c) || !c.containsOnlyTriviaWhiteSpaces)), isChild, location); } - function visitJsxOpeningLikeElementOrFragmentJSX(tagName: Expression, objectProperties: Expression, keyAttr: JsxAttribute | undefined, children: readonly JsxChild[] | undefined, isChild: boolean, location: TextRange) { + function visitJsxOpeningLikeElementOrFragmentJSX(tagName: Expression, objectProperties: Expression, keyAttr: JsxAttribute | undefined, childrenLength: number, isChild: boolean, location: TextRange) { const args: Expression[] = [tagName, objectProperties, !keyAttr ? factory.createVoidZero() : transformJsxAttributeInitializer(keyAttr.initializer)]; if (compilerOptions.jsx === JsxEmit.ReactJSXDev) { const originalFile = getOriginalNode(currentSourceFile); if (originalFile && isSourceFile(originalFile)) { // isStaticChildren development flag - args.push(children && children.length > 1 ? factory.createTrue() : factory.createFalse()); + args.push(childrenLength > 1 ? factory.createTrue() : factory.createFalse()); // __source development flag const lineCol = getLineAndCharacterOfPosition(originalFile, location.pos); args.push(factory.createObjectLiteralExpression([ @@ -264,7 +265,7 @@ namespace ts { args.push(factory.createThis()); } } - const element = setTextRange(factory.createCallExpression(getJsxFactoryCallee(children), /*typeArguments*/ undefined, args), location); + const element = setTextRange(factory.createCallExpression(getJsxFactoryCallee(childrenLength), /*typeArguments*/ undefined, args), location); if (isChild) { startOnNewLine(element); @@ -335,7 +336,7 @@ namespace ts { getImplicitJsxFragmentReference(), childrenProps || factory.createObjectLiteralExpression([]), /*keyAttr*/ undefined, - children, + length(filter(children, c => !isJsxText(c) || !c.containsOnlyTriviaWhiteSpaces)), isChild, location ); diff --git a/tests/baselines/reference/jsxJsxsCjsTransformNestedSelfClosingChild(jsx=react-jsx).js b/tests/baselines/reference/jsxJsxsCjsTransformNestedSelfClosingChild(jsx=react-jsx).js new file mode 100644 index 00000000000..fd5ce7fede7 --- /dev/null +++ b/tests/baselines/reference/jsxJsxsCjsTransformNestedSelfClosingChild(jsx=react-jsx).js @@ -0,0 +1,31 @@ +//// [jsxJsxsCjsTransformNestedSelfClosingChild.tsx] +/// +import type * as React from 'react'; + +console.log( +
+
+
+) + +console.log( +
+
+
+
+) + +console.log( +
+ {[1, 2].map(i =>
{i}
)} +
+) + +//// [jsxJsxsCjsTransformNestedSelfClosingChild.js] +"use strict"; +exports.__esModule = true; +var jsx_runtime_js_1 = require("react/jsx-runtime.js"); +console.log(jsx_runtime_js_1.jsx("div", { children: jsx_runtime_js_1.jsx("div", {}, void 0) }, void 0)); +console.log(jsx_runtime_js_1.jsxs("div", { children: [jsx_runtime_js_1.jsx("div", {}, void 0), + jsx_runtime_js_1.jsx("div", {}, void 0)] }, void 0)); +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)); diff --git a/tests/baselines/reference/jsxJsxsCjsTransformNestedSelfClosingChild(jsx=react-jsx).symbols b/tests/baselines/reference/jsxJsxsCjsTransformNestedSelfClosingChild(jsx=react-jsx).symbols new file mode 100644 index 00000000000..a5b6314f416 --- /dev/null +++ b/tests/baselines/reference/jsxJsxsCjsTransformNestedSelfClosingChild(jsx=react-jsx).symbols @@ -0,0 +1,62 @@ +=== tests/cases/conformance/jsx/jsxs/jsxJsxsCjsTransformNestedSelfClosingChild.tsx === +/// +import type * as React from 'react'; +>React : Symbol(React, Decl(jsxJsxsCjsTransformNestedSelfClosingChild.tsx, 1, 11)) + +console.log( +>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>console : Symbol(console, Decl(lib.dom.d.ts, --, --)) +>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) + +
+>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2420, 114)) + +
+>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2420, 114)) + +
+>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2420, 114)) + +) + +console.log( +>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>console : Symbol(console, Decl(lib.dom.d.ts, --, --)) +>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) + +
+>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2420, 114)) + +
+>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2420, 114)) + +
+>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2420, 114)) + +
+>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2420, 114)) + +) + +console.log( +>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>console : Symbol(console, Decl(lib.dom.d.ts, --, --)) +>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) + +
+>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2420, 114)) + + {[1, 2].map(i =>
{i}
)} +>[1, 2].map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --)) +>map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --)) +>i : Symbol(i, Decl(jsxJsxsCjsTransformNestedSelfClosingChild.tsx, 18, 16)) +>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2420, 114)) +>key : Symbol(key, Decl(jsxJsxsCjsTransformNestedSelfClosingChild.tsx, 18, 25)) +>i : Symbol(i, Decl(jsxJsxsCjsTransformNestedSelfClosingChild.tsx, 18, 16)) +>i : Symbol(i, Decl(jsxJsxsCjsTransformNestedSelfClosingChild.tsx, 18, 16)) +>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2420, 114)) + +
+>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2420, 114)) + +) diff --git a/tests/baselines/reference/jsxJsxsCjsTransformNestedSelfClosingChild(jsx=react-jsx).types b/tests/baselines/reference/jsxJsxsCjsTransformNestedSelfClosingChild(jsx=react-jsx).types new file mode 100644 index 00000000000..07e78fc8dd5 --- /dev/null +++ b/tests/baselines/reference/jsxJsxsCjsTransformNestedSelfClosingChild(jsx=react-jsx).types @@ -0,0 +1,77 @@ +=== tests/cases/conformance/jsx/jsxs/jsxJsxsCjsTransformNestedSelfClosingChild.tsx === +/// +import type * as React from 'react'; +>React : typeof React + +console.log( +>console.log(
) : void +>console.log : (...data: any[]) => void +>console : Console +>log : (...data: any[]) => void + +
+>
: JSX.Element +>div : any + +
+>
: JSX.Element +>div : any + +
+>div : any + +) + +console.log( +>console.log(
) : void +>console.log : (...data: any[]) => void +>console : Console +>log : (...data: any[]) => void + +
+>
: JSX.Element +>div : any + +
+>
: JSX.Element +>div : any + +
+>
: JSX.Element +>div : any + +
+>div : any + +) + +console.log( +>console.log(
{[1, 2].map(i =>
{i}
)}
) : void +>console.log : (...data: any[]) => void +>console : Console +>log : (...data: any[]) => void + +
+>
{[1, 2].map(i =>
{i}
)}
: JSX.Element +>div : any + + {[1, 2].map(i =>
{i}
)} +>[1, 2].map(i =>
{i}
) : JSX.Element[] +>[1, 2].map : (callbackfn: (value: number, index: number, array: number[]) => U, thisArg?: any) => U[] +>[1, 2] : number[] +>1 : 1 +>2 : 2 +>map : (callbackfn: (value: number, index: number, array: number[]) => U, thisArg?: any) => U[] +>i =>
{i}
: (i: number) => JSX.Element +>i : number +>
{i}
: JSX.Element +>div : any +>key : number +>i : number +>i : number +>div : any + +
+>div : any + +) diff --git a/tests/baselines/reference/jsxJsxsCjsTransformNestedSelfClosingChild(jsx=react-jsxdev).js b/tests/baselines/reference/jsxJsxsCjsTransformNestedSelfClosingChild(jsx=react-jsxdev).js new file mode 100644 index 00000000000..0e91bf32b15 --- /dev/null +++ b/tests/baselines/reference/jsxJsxsCjsTransformNestedSelfClosingChild(jsx=react-jsxdev).js @@ -0,0 +1,33 @@ +//// [jsxJsxsCjsTransformNestedSelfClosingChild.tsx] +/// +import type * as React from 'react'; + +console.log( +
+
+
+) + +console.log( +
+
+
+
+) + +console.log( +
+ {[1, 2].map(i =>
{i}
)} +
+) + +//// [jsxJsxsCjsTransformNestedSelfClosingChild.js] +"use strict"; +var _this = this; +exports.__esModule = true; +var jsx_dev_runtime_js_1 = require("react/jsx-dev-runtime.js"); +var _jsxFileName = "tests/cases/conformance/jsx/jsxs/jsxJsxsCjsTransformNestedSelfClosingChild.tsx"; +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)); +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), + 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)); +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)); diff --git a/tests/baselines/reference/jsxJsxsCjsTransformNestedSelfClosingChild(jsx=react-jsxdev).symbols b/tests/baselines/reference/jsxJsxsCjsTransformNestedSelfClosingChild(jsx=react-jsxdev).symbols new file mode 100644 index 00000000000..a5b6314f416 --- /dev/null +++ b/tests/baselines/reference/jsxJsxsCjsTransformNestedSelfClosingChild(jsx=react-jsxdev).symbols @@ -0,0 +1,62 @@ +=== tests/cases/conformance/jsx/jsxs/jsxJsxsCjsTransformNestedSelfClosingChild.tsx === +/// +import type * as React from 'react'; +>React : Symbol(React, Decl(jsxJsxsCjsTransformNestedSelfClosingChild.tsx, 1, 11)) + +console.log( +>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>console : Symbol(console, Decl(lib.dom.d.ts, --, --)) +>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) + +
+>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2420, 114)) + +
+>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2420, 114)) + +
+>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2420, 114)) + +) + +console.log( +>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>console : Symbol(console, Decl(lib.dom.d.ts, --, --)) +>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) + +
+>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2420, 114)) + +
+>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2420, 114)) + +
+>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2420, 114)) + +
+>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2420, 114)) + +) + +console.log( +>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>console : Symbol(console, Decl(lib.dom.d.ts, --, --)) +>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) + +
+>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2420, 114)) + + {[1, 2].map(i =>
{i}
)} +>[1, 2].map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --)) +>map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --)) +>i : Symbol(i, Decl(jsxJsxsCjsTransformNestedSelfClosingChild.tsx, 18, 16)) +>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2420, 114)) +>key : Symbol(key, Decl(jsxJsxsCjsTransformNestedSelfClosingChild.tsx, 18, 25)) +>i : Symbol(i, Decl(jsxJsxsCjsTransformNestedSelfClosingChild.tsx, 18, 16)) +>i : Symbol(i, Decl(jsxJsxsCjsTransformNestedSelfClosingChild.tsx, 18, 16)) +>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2420, 114)) + +
+>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2420, 114)) + +) diff --git a/tests/baselines/reference/jsxJsxsCjsTransformNestedSelfClosingChild(jsx=react-jsxdev).types b/tests/baselines/reference/jsxJsxsCjsTransformNestedSelfClosingChild(jsx=react-jsxdev).types new file mode 100644 index 00000000000..07e78fc8dd5 --- /dev/null +++ b/tests/baselines/reference/jsxJsxsCjsTransformNestedSelfClosingChild(jsx=react-jsxdev).types @@ -0,0 +1,77 @@ +=== tests/cases/conformance/jsx/jsxs/jsxJsxsCjsTransformNestedSelfClosingChild.tsx === +/// +import type * as React from 'react'; +>React : typeof React + +console.log( +>console.log(
) : void +>console.log : (...data: any[]) => void +>console : Console +>log : (...data: any[]) => void + +
+>
: JSX.Element +>div : any + +
+>
: JSX.Element +>div : any + +
+>div : any + +) + +console.log( +>console.log(
) : void +>console.log : (...data: any[]) => void +>console : Console +>log : (...data: any[]) => void + +
+>
: JSX.Element +>div : any + +
+>
: JSX.Element +>div : any + +
+>
: JSX.Element +>div : any + +
+>div : any + +) + +console.log( +>console.log(
{[1, 2].map(i =>
{i}
)}
) : void +>console.log : (...data: any[]) => void +>console : Console +>log : (...data: any[]) => void + +
+>
{[1, 2].map(i =>
{i}
)}
: JSX.Element +>div : any + + {[1, 2].map(i =>
{i}
)} +>[1, 2].map(i =>
{i}
) : JSX.Element[] +>[1, 2].map : (callbackfn: (value: number, index: number, array: number[]) => U, thisArg?: any) => U[] +>[1, 2] : number[] +>1 : 1 +>2 : 2 +>map : (callbackfn: (value: number, index: number, array: number[]) => U, thisArg?: any) => U[] +>i =>
{i}
: (i: number) => JSX.Element +>i : number +>
{i}
: JSX.Element +>div : any +>key : number +>i : number +>i : number +>div : any + +
+>div : any + +) diff --git a/tests/cases/conformance/jsx/jsxs/jsxJsxsCjsTransformNestedSelfClosingChild.tsx b/tests/cases/conformance/jsx/jsxs/jsxJsxsCjsTransformNestedSelfClosingChild.tsx new file mode 100644 index 00000000000..25f3fb738a7 --- /dev/null +++ b/tests/cases/conformance/jsx/jsxs/jsxJsxsCjsTransformNestedSelfClosingChild.tsx @@ -0,0 +1,24 @@ +// @jsx: react-jsx,react-jsxdev +// @strict: true +// @module: commonjs +/// +import type * as React from 'react'; + +console.log( +
+
+
+) + +console.log( +
+
+
+
+) + +console.log( +
+ {[1, 2].map(i =>
{i}
)} +
+) \ No newline at end of file