Skip to content

Commit 8b728c8

Browse files
authored
Merge pull request #41151 from Andarist/fix/create-element-new-jsx-transform
Fix/create element new jsx transform
2 parents c5cbd52 + 0ad22de commit 8b728c8

23 files changed

+734
-32
lines changed

src/compiler/factory/utilities.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ namespace ts {
4646
}
4747
}
4848

49-
function createJsxFactoryExpression(factory: NodeFactory, jsxFactoryEntity: EntityName | undefined, reactNamespace: string, parent: JsxOpeningLikeElement | JsxOpeningFragment): Expression {
49+
export function createJsxFactoryExpression(factory: NodeFactory, jsxFactoryEntity: EntityName | undefined, reactNamespace: string, parent: JsxOpeningLikeElement | JsxOpeningFragment): Expression {
5050
return jsxFactoryEntity ?
5151
createJsxFactoryExpressionFromEntityName(factory, jsxFactoryEntity, parent) :
5252
factory.createPropertyAccessExpression(
@@ -64,7 +64,7 @@ namespace ts {
6464
);
6565
}
6666

67-
export function createExpressionForJsxElement(factory: NodeFactory, jsxFactoryEntity: EntityName | undefined, reactNamespace: string, tagName: Expression, props: Expression | undefined, children: readonly Expression[] | undefined, parentElement: JsxOpeningLikeElement, location: TextRange): LeftHandSideExpression {
67+
export function createExpressionForJsxElement(factory: NodeFactory, callee: Expression, tagName: Expression, props: Expression | undefined, children: readonly Expression[] | undefined, location: TextRange): LeftHandSideExpression {
6868
const argumentsList = [tagName];
6969
if (props) {
7070
argumentsList.push(props);
@@ -88,7 +88,7 @@ namespace ts {
8888

8989
return setTextRange(
9090
factory.createCallExpression(
91-
createJsxFactoryExpression(factory, jsxFactoryEntity, reactNamespace, parentElement),
91+
callee,
9292
/*typeArguments*/ undefined,
9393
argumentsList
9494
),

src/compiler/transformers/jsx.ts

+45-29
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ namespace ts {
44
interface PerFileState {
55
importSpecifier?: string;
66
filenameDeclaration?: VariableDeclaration & { name: Identifier; };
7-
utilizedImplicitRuntimeImports?: Map<ImportSpecifier>;
7+
utilizedImplicitRuntimeImports?: Map<Map<ImportSpecifier>>;
88
}
99

1010
const {
@@ -40,17 +40,25 @@ namespace ts {
4040
}
4141

4242
function getImplicitImportForName(name: string) {
43-
const existing = currentFileState.utilizedImplicitRuntimeImports?.get(name);
43+
const importSource = name === "createElement"
44+
? currentFileState.importSpecifier!
45+
: `${currentFileState.importSpecifier}/${compilerOptions.jsx === JsxEmit.ReactJSXDev ? "jsx-dev-runtime" : "jsx-runtime"}`;
46+
const existing = currentFileState.utilizedImplicitRuntimeImports?.get(importSource)?.get(name);
4447
if (existing) {
4548
return existing.name;
4649
}
4750
if (!currentFileState.utilizedImplicitRuntimeImports) {
4851
currentFileState.utilizedImplicitRuntimeImports = createMap();
4952
}
53+
let specifierSourceImports = currentFileState.utilizedImplicitRuntimeImports.get(importSource);
54+
if (!specifierSourceImports) {
55+
specifierSourceImports = createMap();
56+
currentFileState.utilizedImplicitRuntimeImports.set(importSource, specifierSourceImports);
57+
}
5058
const generatedName = factory.createUniqueName(`_${name}`, GeneratedIdentifierFlags.Optimistic | GeneratedIdentifierFlags.FileLevel | GeneratedIdentifierFlags.AllowNameSubstitution);
5159
const specifier = factory.createImportSpecifier(factory.createIdentifier(name), generatedName);
5260
generatedName.generatedImportReference = specifier;
53-
currentFileState.utilizedImplicitRuntimeImports.set(name, specifier);
61+
specifierSourceImports.set(name, specifier);
5462
return generatedName;
5563
}
5664

@@ -73,29 +81,30 @@ namespace ts {
7381
if (currentFileState.filenameDeclaration) {
7482
statements = insertStatementAfterCustomPrologue(statements.slice(), factory.createVariableStatement(/*modifiers*/ undefined, factory.createVariableDeclarationList([currentFileState.filenameDeclaration], NodeFlags.Const)));
7583
}
76-
if (currentFileState.utilizedImplicitRuntimeImports && currentFileState.utilizedImplicitRuntimeImports.size && currentFileState.importSpecifier !== undefined) {
77-
const specifier = `${currentFileState.importSpecifier}/${compilerOptions.jsx === JsxEmit.ReactJSXDev ? "jsx-dev-runtime" : "jsx-runtime"}`;
78-
if (isExternalModule(node)) {
79-
// Add `import` statement
80-
const importStatement = factory.createImportDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, factory.createImportClause(/*typeOnly*/ false, /*name*/ undefined, factory.createNamedImports(arrayFrom(currentFileState.utilizedImplicitRuntimeImports.values()))), factory.createStringLiteral(specifier));
81-
setParentRecursive(importStatement, /*incremental*/ false);
82-
statements = insertStatementAfterCustomPrologue(statements.slice(), importStatement);
83-
}
84-
else if (isExternalOrCommonJsModule(node)) {
85-
// Add `require` statement
86-
const requireStatement = factory.createVariableStatement(/*modifiers*/ undefined, factory.createVariableDeclarationList([
87-
factory.createVariableDeclaration(
88-
factory.createObjectBindingPattern(map(arrayFrom(currentFileState.utilizedImplicitRuntimeImports.values()), s => factory.createBindingElement(/*dotdotdot*/ undefined, s.propertyName, s.name))),
89-
/*exclaimationToken*/ undefined,
90-
/*type*/ undefined,
91-
factory.createCallExpression(factory.createIdentifier("require"), /*typeArguments*/ undefined, [factory.createStringLiteral(specifier)])
92-
)
93-
], NodeFlags.Const));
94-
setParentRecursive(requireStatement, /*incremental*/ false);
95-
statements = insertStatementAfterCustomPrologue(statements.slice(), requireStatement);
96-
}
97-
else {
98-
// Do nothing (script file) - consider an error in the checker?
84+
if (currentFileState.utilizedImplicitRuntimeImports) {
85+
for (const [importSource, importSpecifiersMap] of arrayFrom(currentFileState.utilizedImplicitRuntimeImports.entries())) {
86+
if (isExternalModule(node)) {
87+
// Add `import` statement
88+
const importStatement = factory.createImportDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, factory.createImportClause(/*typeOnly*/ false, /*name*/ undefined, factory.createNamedImports(arrayFrom(importSpecifiersMap.values()))), factory.createStringLiteral(importSource));
89+
setParentRecursive(importStatement, /*incremental*/ false);
90+
statements = insertStatementAfterCustomPrologue(statements.slice(), importStatement);
91+
}
92+
else if (isExternalOrCommonJsModule(node)) {
93+
// Add `require` statement
94+
const requireStatement = factory.createVariableStatement(/*modifiers*/ undefined, factory.createVariableDeclarationList([
95+
factory.createVariableDeclaration(
96+
factory.createObjectBindingPattern(map(arrayFrom(importSpecifiersMap.values()), s => factory.createBindingElement(/*dotdotdot*/ undefined, s.propertyName, s.name))),
97+
/*exclaimationToken*/ undefined,
98+
/*type*/ undefined,
99+
factory.createCallExpression(factory.createIdentifier("require"), /*typeArguments*/ undefined, [factory.createStringLiteral(importSource)])
100+
)
101+
], NodeFlags.Const));
102+
setParentRecursive(requireStatement, /*incremental*/ false);
103+
statements = insertStatementAfterCustomPrologue(statements.slice(), requireStatement);
104+
}
105+
else {
106+
// Do nothing (script file) - consider an error in the checker?
107+
}
99108
}
100109
}
101110
if (statements !== visited.statements) {
@@ -306,14 +315,21 @@ namespace ts {
306315
}
307316
}
308317

318+
const callee = currentFileState.importSpecifier === undefined
319+
? createJsxFactoryExpression(
320+
factory,
321+
context.getEmitResolver().getJsxFactoryEntity(currentSourceFile),
322+
compilerOptions.reactNamespace!, // TODO: GH#18217
323+
node
324+
)
325+
: getImplicitImportForName("createElement");
326+
309327
const element = createExpressionForJsxElement(
310328
factory,
311-
context.getEmitResolver().getJsxFactoryEntity(currentSourceFile),
312-
compilerOptions.reactNamespace!, // TODO: GH#18217
329+
callee,
313330
tagName,
314331
objectProperties,
315332
mapDefined(children, transformJsxChildToExpression),
316-
node,
317333
location
318334
);
319335

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
//// [jsxJsxsCjsTransformKeyProp.tsx]
2+
/// <reference path="/.lib/react16.d.ts" />
3+
const props = { answer: 42 }
4+
const a = <div key="foo" {...props}>text</div>;
5+
const b = <div {...props} key="bar">text</div>;
6+
7+
export {};
8+
9+
10+
//// [jsxJsxsCjsTransformKeyProp.js]
11+
"use strict";
12+
var __assign = (this && this.__assign) || function () {
13+
__assign = Object.assign || function(t) {
14+
for (var s, i = 1, n = arguments.length; i < n; i++) {
15+
s = arguments[i];
16+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
17+
t[p] = s[p];
18+
}
19+
return t;
20+
};
21+
return __assign.apply(this, arguments);
22+
};
23+
exports.__esModule = true;
24+
var react_1 = require("react");
25+
var jsx_runtime_1 = require("react/jsx-runtime");
26+
/// <reference path="react16.d.ts" />
27+
var props = { answer: 42 };
28+
var a = jsx_runtime_1.jsx("div", __assign({}, props, { children: "text" }), "foo");
29+
var b = react_1.createElement("div", __assign({}, props, { key: "bar" }), "text");
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
=== tests/cases/conformance/jsx/jsxs/jsxJsxsCjsTransformKeyProp.tsx ===
2+
/// <reference path="react16.d.ts" />
3+
const props = { answer: 42 }
4+
>props : Symbol(props, Decl(jsxJsxsCjsTransformKeyProp.tsx, 1, 5))
5+
>answer : Symbol(answer, Decl(jsxJsxsCjsTransformKeyProp.tsx, 1, 15))
6+
7+
const a = <div key="foo" {...props}>text</div>;
8+
>a : Symbol(a, Decl(jsxJsxsCjsTransformKeyProp.tsx, 2, 5))
9+
>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2420, 114))
10+
>key : Symbol(key, Decl(jsxJsxsCjsTransformKeyProp.tsx, 2, 14))
11+
>props : Symbol(props, Decl(jsxJsxsCjsTransformKeyProp.tsx, 1, 5))
12+
>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2420, 114))
13+
14+
const b = <div {...props} key="bar">text</div>;
15+
>b : Symbol(b, Decl(jsxJsxsCjsTransformKeyProp.tsx, 3, 5))
16+
>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2420, 114))
17+
>props : Symbol(props, Decl(jsxJsxsCjsTransformKeyProp.tsx, 1, 5))
18+
>key : Symbol(key, Decl(jsxJsxsCjsTransformKeyProp.tsx, 3, 25))
19+
>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2420, 114))
20+
21+
export {};
22+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
=== tests/cases/conformance/jsx/jsxs/jsxJsxsCjsTransformKeyProp.tsx ===
2+
/// <reference path="react16.d.ts" />
3+
const props = { answer: 42 }
4+
>props : { answer: number; }
5+
>{ answer: 42 } : { answer: number; }
6+
>answer : number
7+
>42 : 42
8+
9+
const a = <div key="foo" {...props}>text</div>;
10+
>a : JSX.Element
11+
><div key="foo" {...props}>text</div> : JSX.Element
12+
>div : any
13+
>key : string
14+
>props : { answer: number; }
15+
>div : any
16+
17+
const b = <div {...props} key="bar">text</div>;
18+
>b : JSX.Element
19+
><div {...props} key="bar">text</div> : JSX.Element
20+
>div : any
21+
>props : { answer: number; }
22+
>key : string
23+
>div : any
24+
25+
export {};
26+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
//// [jsxJsxsCjsTransformKeyProp.tsx]
2+
/// <reference path="/.lib/react16.d.ts" />
3+
const props = { answer: 42 }
4+
const a = <div key="foo" {...props}>text</div>;
5+
const b = <div {...props} key="bar">text</div>;
6+
7+
export {};
8+
9+
10+
//// [jsxJsxsCjsTransformKeyProp.js]
11+
"use strict";
12+
var __assign = (this && this.__assign) || function () {
13+
__assign = Object.assign || function(t) {
14+
for (var s, i = 1, n = arguments.length; i < n; i++) {
15+
s = arguments[i];
16+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
17+
t[p] = s[p];
18+
}
19+
return t;
20+
};
21+
return __assign.apply(this, arguments);
22+
};
23+
exports.__esModule = true;
24+
var react_1 = require("react");
25+
var jsx_dev_runtime_1 = require("react/jsx-dev-runtime");
26+
var _jsxFileName = "tests/cases/conformance/jsx/jsxs/jsxJsxsCjsTransformKeyProp.tsx";
27+
/// <reference path="react16.d.ts" />
28+
var props = { answer: 42 };
29+
var a = jsx_dev_runtime_1.jsxDEV("div", __assign({}, props, { children: "text" }), "foo", false, { fileName: _jsxFileName, lineNumber: 3, columnNumber: 10 }, this);
30+
var b = react_1.createElement("div", __assign({}, props, { key: "bar" }), "text");
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
=== tests/cases/conformance/jsx/jsxs/jsxJsxsCjsTransformKeyProp.tsx ===
2+
/// <reference path="react16.d.ts" />
3+
const props = { answer: 42 }
4+
>props : Symbol(props, Decl(jsxJsxsCjsTransformKeyProp.tsx, 1, 5))
5+
>answer : Symbol(answer, Decl(jsxJsxsCjsTransformKeyProp.tsx, 1, 15))
6+
7+
const a = <div key="foo" {...props}>text</div>;
8+
>a : Symbol(a, Decl(jsxJsxsCjsTransformKeyProp.tsx, 2, 5))
9+
>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2420, 114))
10+
>key : Symbol(key, Decl(jsxJsxsCjsTransformKeyProp.tsx, 2, 14))
11+
>props : Symbol(props, Decl(jsxJsxsCjsTransformKeyProp.tsx, 1, 5))
12+
>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2420, 114))
13+
14+
const b = <div {...props} key="bar">text</div>;
15+
>b : Symbol(b, Decl(jsxJsxsCjsTransformKeyProp.tsx, 3, 5))
16+
>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2420, 114))
17+
>props : Symbol(props, Decl(jsxJsxsCjsTransformKeyProp.tsx, 1, 5))
18+
>key : Symbol(key, Decl(jsxJsxsCjsTransformKeyProp.tsx, 3, 25))
19+
>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2420, 114))
20+
21+
export {};
22+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
=== tests/cases/conformance/jsx/jsxs/jsxJsxsCjsTransformKeyProp.tsx ===
2+
/// <reference path="react16.d.ts" />
3+
const props = { answer: 42 }
4+
>props : { answer: number; }
5+
>{ answer: 42 } : { answer: number; }
6+
>answer : number
7+
>42 : 42
8+
9+
const a = <div key="foo" {...props}>text</div>;
10+
>a : JSX.Element
11+
><div key="foo" {...props}>text</div> : JSX.Element
12+
>div : any
13+
>key : string
14+
>props : { answer: number; }
15+
>div : any
16+
17+
const b = <div {...props} key="bar">text</div>;
18+
>b : JSX.Element
19+
><div {...props} key="bar">text</div> : JSX.Element
20+
>div : any
21+
>props : { answer: number; }
22+
>key : string
23+
>div : any
24+
25+
export {};
26+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
//// [jsxJsxsCjsTransformKeyPropCustomImport.tsx]
2+
/// <reference path="/.lib/react16.d.ts" />
3+
const props = { answer: 42 }
4+
const a = <div key="foo" {...props}>text</div>;
5+
const b = <div {...props} key="bar">text</div>;
6+
7+
export {};
8+
9+
10+
//// [jsxJsxsCjsTransformKeyPropCustomImport.js]
11+
"use strict";
12+
var __assign = (this && this.__assign) || function () {
13+
__assign = Object.assign || function(t) {
14+
for (var s, i = 1, n = arguments.length; i < n; i++) {
15+
s = arguments[i];
16+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
17+
t[p] = s[p];
18+
}
19+
return t;
20+
};
21+
return __assign.apply(this, arguments);
22+
};
23+
exports.__esModule = true;
24+
var preact_1 = require("preact");
25+
var jsx_runtime_1 = require("preact/jsx-runtime");
26+
/// <reference path="react16.d.ts" />
27+
var props = { answer: 42 };
28+
var a = jsx_runtime_1.jsx("div", __assign({}, props, { children: "text" }), "foo");
29+
var b = preact_1.createElement("div", __assign({}, props, { key: "bar" }), "text");
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
=== tests/cases/conformance/jsx/jsxs/jsxJsxsCjsTransformKeyPropCustomImport.tsx ===
2+
/// <reference path="react16.d.ts" />
3+
const props = { answer: 42 }
4+
>props : Symbol(props, Decl(jsxJsxsCjsTransformKeyPropCustomImport.tsx, 1, 5))
5+
>answer : Symbol(answer, Decl(jsxJsxsCjsTransformKeyPropCustomImport.tsx, 1, 15))
6+
7+
const a = <div key="foo" {...props}>text</div>;
8+
>a : Symbol(a, Decl(jsxJsxsCjsTransformKeyPropCustomImport.tsx, 2, 5))
9+
>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2420, 114))
10+
>key : Symbol(key, Decl(jsxJsxsCjsTransformKeyPropCustomImport.tsx, 2, 14))
11+
>props : Symbol(props, Decl(jsxJsxsCjsTransformKeyPropCustomImport.tsx, 1, 5))
12+
>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2420, 114))
13+
14+
const b = <div {...props} key="bar">text</div>;
15+
>b : Symbol(b, Decl(jsxJsxsCjsTransformKeyPropCustomImport.tsx, 3, 5))
16+
>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2420, 114))
17+
>props : Symbol(props, Decl(jsxJsxsCjsTransformKeyPropCustomImport.tsx, 1, 5))
18+
>key : Symbol(key, Decl(jsxJsxsCjsTransformKeyPropCustomImport.tsx, 3, 25))
19+
>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2420, 114))
20+
21+
export {};
22+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
=== tests/cases/conformance/jsx/jsxs/jsxJsxsCjsTransformKeyPropCustomImport.tsx ===
2+
/// <reference path="react16.d.ts" />
3+
const props = { answer: 42 }
4+
>props : { answer: number; }
5+
>{ answer: 42 } : { answer: number; }
6+
>answer : number
7+
>42 : 42
8+
9+
const a = <div key="foo" {...props}>text</div>;
10+
>a : JSX.Element
11+
><div key="foo" {...props}>text</div> : JSX.Element
12+
>div : any
13+
>key : string
14+
>props : { answer: number; }
15+
>div : any
16+
17+
const b = <div {...props} key="bar">text</div>;
18+
>b : JSX.Element
19+
><div {...props} key="bar">text</div> : JSX.Element
20+
>div : any
21+
>props : { answer: number; }
22+
>key : string
23+
>div : any
24+
25+
export {};
26+

0 commit comments

Comments
 (0)