Skip to content

Commit 9fd2c2c

Browse files
committed
Add auto-import for createElement when using the new JSX transform
1 parent 43433eb commit 9fd2c2c

8 files changed

+64
-40
lines changed

src/compiler/factory/utilities.ts

Lines changed: 3 additions & 3 deletions
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

Lines changed: 45 additions & 29 deletions
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.js" : "jsx-runtime.js"}`;
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.js" : "jsx-runtime.js"}`;
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

tests/baselines/reference/jsxJsxsCjsTransformKeyProp(jsx=react-jsx).js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,9 @@ var __assign = (this && this.__assign) || function () {
2121
return __assign.apply(this, arguments);
2222
};
2323
exports.__esModule = true;
24+
var react_1 = require("react");
2425
var jsx_runtime_js_1 = require("react/jsx-runtime.js");
2526
/// <reference path="react16.d.ts" />
2627
var props = { answer: 42 };
2728
var a = jsx_runtime_js_1.jsx("div", __assign({}, props, { children: "text" }), "foo");
28-
var b = React.createElement("div", __assign({}, props, { key: "bar" }), "text");
29+
var b = react_1.createElement("div", __assign({}, props, { key: "bar" }), "text");

tests/baselines/reference/jsxJsxsCjsTransformKeyProp(jsx=react-jsxdev).js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,10 @@ var __assign = (this && this.__assign) || function () {
2121
return __assign.apply(this, arguments);
2222
};
2323
exports.__esModule = true;
24+
var react_1 = require("react");
2425
var jsx_dev_runtime_js_1 = require("react/jsx-dev-runtime.js");
2526
var _jsxFileName = "tests/cases/conformance/jsx/jsxs/jsxJsxsCjsTransformKeyProp.tsx";
2627
/// <reference path="react16.d.ts" />
2728
var props = { answer: 42 };
2829
var a = jsx_dev_runtime_js_1.jsxDEV("div", __assign({}, props, { children: "text" }), "foo", false, { fileName: _jsxFileName, lineNumber: 3, columnNumber: 10 }, this);
29-
var b = React.createElement("div", __assign({}, props, { key: "bar" }), "text");
30+
var b = react_1.createElement("div", __assign({}, props, { key: "bar" }), "text");

tests/baselines/reference/jsxJsxsCjsTransformKeyPropCustomImport(jsx=react-jsx).js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,9 @@ var __assign = (this && this.__assign) || function () {
2121
return __assign.apply(this, arguments);
2222
};
2323
exports.__esModule = true;
24+
var preact_1 = require("preact");
2425
var jsx_runtime_js_1 = require("preact/jsx-runtime.js");
2526
/// <reference path="react16.d.ts" />
2627
var props = { answer: 42 };
2728
var a = jsx_runtime_js_1.jsx("div", __assign({}, props, { children: "text" }), "foo");
28-
var b = React.createElement("div", __assign({}, props, { key: "bar" }), "text");
29+
var b = preact_1.createElement("div", __assign({}, props, { key: "bar" }), "text");

tests/baselines/reference/jsxJsxsCjsTransformKeyPropCustomImport(jsx=react-jsxdev).js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,10 @@ var __assign = (this && this.__assign) || function () {
2121
return __assign.apply(this, arguments);
2222
};
2323
exports.__esModule = true;
24+
var preact_1 = require("preact");
2425
var jsx_dev_runtime_js_1 = require("preact/jsx-dev-runtime.js");
2526
var _jsxFileName = "tests/cases/conformance/jsx/jsxs/jsxJsxsCjsTransformKeyPropCustomImport.tsx";
2627
/// <reference path="react16.d.ts" />
2728
var props = { answer: 42 };
2829
var a = jsx_dev_runtime_js_1.jsxDEV("div", __assign({}, props, { children: "text" }), "foo", false, { fileName: _jsxFileName, lineNumber: 3, columnNumber: 10 }, this);
29-
var b = React.createElement("div", __assign({}, props, { key: "bar" }), "text");
30+
var b = preact_1.createElement("div", __assign({}, props, { key: "bar" }), "text");

tests/baselines/reference/jsxJsxsCjsTransformKeyPropCustomImportPragma(jsx=react-jsx).js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,13 @@ var __assign = (this && this.__assign) || function () {
3434
return __assign.apply(this, arguments);
3535
};
3636
exports.__esModule = true;
37+
var preact_1 = require("preact");
3738
var jsx_runtime_js_1 = require("preact/jsx-runtime.js");
3839
/// <reference path="react16.d.ts" />
3940
/* @jsxImportSource preact */
4041
var props = { answer: 42 };
4142
var a = jsx_runtime_js_1.jsx("div", __assign({}, props, { children: "text" }), "foo");
42-
var b = React.createElement("div", __assign({}, props, { key: "bar" }), "text");
43+
var b = preact_1.createElement("div", __assign({}, props, { key: "bar" }), "text");
4344
//// [react.js]
4445
"use strict";
4546
var __assign = (this && this.__assign) || function () {
@@ -54,10 +55,11 @@ var __assign = (this && this.__assign) || function () {
5455
return __assign.apply(this, arguments);
5556
};
5657
exports.__esModule = true;
58+
var react_1 = require("react");
5759
var jsx_runtime_js_1 = require("react/jsx-runtime.js");
5860
/// <reference path="react16.d.ts" />
5961
/* @jsxImportSource react */
6062
require("./preact");
6163
var props2 = { answer: 42 };
6264
var a2 = jsx_runtime_js_1.jsx("div", __assign({}, props2, { children: "text" }), "foo");
63-
var b2 = React.createElement("div", __assign({}, props2, { key: "bar" }), "text");
65+
var b2 = react_1.createElement("div", __assign({}, props2, { key: "bar" }), "text");

tests/baselines/reference/jsxJsxsCjsTransformKeyPropCustomImportPragma(jsx=react-jsxdev).js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,14 @@ var __assign = (this && this.__assign) || function () {
3434
return __assign.apply(this, arguments);
3535
};
3636
exports.__esModule = true;
37+
var preact_1 = require("preact");
3738
var jsx_dev_runtime_js_1 = require("preact/jsx-dev-runtime.js");
3839
var _jsxFileName = "tests/cases/conformance/jsx/jsxs/preact.tsx";
3940
/// <reference path="react16.d.ts" />
4041
/* @jsxImportSource preact */
4142
var props = { answer: 42 };
4243
var a = jsx_dev_runtime_js_1.jsxDEV("div", __assign({}, props, { children: "text" }), "foo", false, { fileName: _jsxFileName, lineNumber: 4, columnNumber: 10 }, this);
43-
var b = React.createElement("div", __assign({}, props, { key: "bar" }), "text");
44+
var b = preact_1.createElement("div", __assign({}, props, { key: "bar" }), "text");
4445
//// [react.js]
4546
"use strict";
4647
var __assign = (this && this.__assign) || function () {
@@ -55,11 +56,12 @@ var __assign = (this && this.__assign) || function () {
5556
return __assign.apply(this, arguments);
5657
};
5758
exports.__esModule = true;
59+
var react_1 = require("react");
5860
var jsx_dev_runtime_js_1 = require("react/jsx-dev-runtime.js");
5961
var _jsxFileName = "tests/cases/conformance/jsx/jsxs/react.tsx";
6062
/// <reference path="react16.d.ts" />
6163
/* @jsxImportSource react */
6264
require("./preact");
6365
var props2 = { answer: 42 };
6466
var a2 = jsx_dev_runtime_js_1.jsxDEV("div", __assign({}, props2, { children: "text" }), "foo", false, { fileName: _jsxFileName, lineNumber: 5, columnNumber: 11 }, this);
65-
var b2 = React.createElement("div", __assign({}, props2, { key: "bar" }), "text");
67+
var b2 = react_1.createElement("div", __assign({}, props2, { key: "bar" }), "text");

0 commit comments

Comments
 (0)