diff --git a/src/compiler/factory/utilities.ts b/src/compiler/factory/utilities.ts index 770600d94b7..0960509ae4d 100644 --- a/src/compiler/factory/utilities.ts +++ b/src/compiler/factory/utilities.ts @@ -46,7 +46,7 @@ namespace ts { } } - function createJsxFactoryExpression(factory: NodeFactory, jsxFactoryEntity: EntityName | undefined, reactNamespace: string, parent: JsxOpeningLikeElement | JsxOpeningFragment): Expression { + export function createJsxFactoryExpression(factory: NodeFactory, jsxFactoryEntity: EntityName | undefined, reactNamespace: string, parent: JsxOpeningLikeElement | JsxOpeningFragment): Expression { return jsxFactoryEntity ? createJsxFactoryExpressionFromEntityName(factory, jsxFactoryEntity, parent) : factory.createPropertyAccessExpression( @@ -64,7 +64,7 @@ namespace ts { ); } - export function createExpressionForJsxElement(factory: NodeFactory, jsxFactoryEntity: EntityName | undefined, reactNamespace: string, tagName: Expression, props: Expression | undefined, children: readonly Expression[] | undefined, parentElement: JsxOpeningLikeElement, location: TextRange): LeftHandSideExpression { + export function createExpressionForJsxElement(factory: NodeFactory, callee: Expression, tagName: Expression, props: Expression | undefined, children: readonly Expression[] | undefined, location: TextRange): LeftHandSideExpression { const argumentsList = [tagName]; if (props) { argumentsList.push(props); @@ -88,7 +88,7 @@ namespace ts { return setTextRange( factory.createCallExpression( - createJsxFactoryExpression(factory, jsxFactoryEntity, reactNamespace, parentElement), + callee, /*typeArguments*/ undefined, argumentsList ), diff --git a/src/compiler/transformers/jsx.ts b/src/compiler/transformers/jsx.ts index 905bfad32e0..fb9d16d6b94 100644 --- a/src/compiler/transformers/jsx.ts +++ b/src/compiler/transformers/jsx.ts @@ -4,7 +4,7 @@ namespace ts { interface PerFileState { importSpecifier?: string; filenameDeclaration?: VariableDeclaration & { name: Identifier; }; - utilizedImplicitRuntimeImports?: Map; + utilizedImplicitRuntimeImports?: Map>; } const { @@ -40,17 +40,25 @@ namespace ts { } function getImplicitImportForName(name: string) { - const existing = currentFileState.utilizedImplicitRuntimeImports?.get(name); + const importSource = name === "createElement" + ? currentFileState.importSpecifier! + : `${currentFileState.importSpecifier}/${compilerOptions.jsx === JsxEmit.ReactJSXDev ? "jsx-dev-runtime.js" : "jsx-runtime.js"}`; + const existing = currentFileState.utilizedImplicitRuntimeImports?.get(importSource)?.get(name); if (existing) { return existing.name; } if (!currentFileState.utilizedImplicitRuntimeImports) { currentFileState.utilizedImplicitRuntimeImports = createMap(); } + let specifierSourceImports = currentFileState.utilizedImplicitRuntimeImports.get(importSource); + if (!specifierSourceImports) { + specifierSourceImports = createMap(); + currentFileState.utilizedImplicitRuntimeImports.set(importSource, specifierSourceImports); + } const generatedName = factory.createUniqueName(`_${name}`, GeneratedIdentifierFlags.Optimistic | GeneratedIdentifierFlags.FileLevel | GeneratedIdentifierFlags.AllowNameSubstitution); const specifier = factory.createImportSpecifier(factory.createIdentifier(name), generatedName); generatedName.generatedImportReference = specifier; - currentFileState.utilizedImplicitRuntimeImports.set(name, specifier); + specifierSourceImports.set(name, specifier); return generatedName; } @@ -73,29 +81,30 @@ namespace ts { if (currentFileState.filenameDeclaration) { statements = insertStatementAfterCustomPrologue(statements.slice(), factory.createVariableStatement(/*modifiers*/ undefined, factory.createVariableDeclarationList([currentFileState.filenameDeclaration], NodeFlags.Const))); } - if (currentFileState.utilizedImplicitRuntimeImports && currentFileState.utilizedImplicitRuntimeImports.size && currentFileState.importSpecifier !== undefined) { - const specifier = `${currentFileState.importSpecifier}/${compilerOptions.jsx === JsxEmit.ReactJSXDev ? "jsx-dev-runtime.js" : "jsx-runtime.js"}`; - if (isExternalModule(node)) { - // Add `import` statement - const importStatement = factory.createImportDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, factory.createImportClause(/*typeOnly*/ false, /*name*/ undefined, factory.createNamedImports(arrayFrom(currentFileState.utilizedImplicitRuntimeImports.values()))), factory.createStringLiteral(specifier)); - setParentRecursive(importStatement, /*incremental*/ false); - statements = insertStatementAfterCustomPrologue(statements.slice(), importStatement); - } - else if (isExternalOrCommonJsModule(node)) { - // Add `require` statement - const requireStatement = factory.createVariableStatement(/*modifiers*/ undefined, factory.createVariableDeclarationList([ - factory.createVariableDeclaration( - factory.createObjectBindingPattern(map(arrayFrom(currentFileState.utilizedImplicitRuntimeImports.values()), s => factory.createBindingElement(/*dotdotdot*/ undefined, s.propertyName, s.name))), - /*exclaimationToken*/ undefined, - /*type*/ undefined, - factory.createCallExpression(factory.createIdentifier("require"), /*typeArguments*/ undefined, [factory.createStringLiteral(specifier)]) - ) - ], NodeFlags.Const)); - setParentRecursive(requireStatement, /*incremental*/ false); - statements = insertStatementAfterCustomPrologue(statements.slice(), requireStatement); - } - else { - // Do nothing (script file) - consider an error in the checker? + if (currentFileState.utilizedImplicitRuntimeImports) { + for (const [importSource, importSpecifiersMap] of arrayFrom(currentFileState.utilizedImplicitRuntimeImports.entries())) { + if (isExternalModule(node)) { + // Add `import` statement + const importStatement = factory.createImportDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, factory.createImportClause(/*typeOnly*/ false, /*name*/ undefined, factory.createNamedImports(arrayFrom(importSpecifiersMap.values()))), factory.createStringLiteral(importSource)); + setParentRecursive(importStatement, /*incremental*/ false); + statements = insertStatementAfterCustomPrologue(statements.slice(), importStatement); + } + else if (isExternalOrCommonJsModule(node)) { + // Add `require` statement + const requireStatement = factory.createVariableStatement(/*modifiers*/ undefined, factory.createVariableDeclarationList([ + factory.createVariableDeclaration( + factory.createObjectBindingPattern(map(arrayFrom(importSpecifiersMap.values()), s => factory.createBindingElement(/*dotdotdot*/ undefined, s.propertyName, s.name))), + /*exclaimationToken*/ undefined, + /*type*/ undefined, + factory.createCallExpression(factory.createIdentifier("require"), /*typeArguments*/ undefined, [factory.createStringLiteral(importSource)]) + ) + ], NodeFlags.Const)); + setParentRecursive(requireStatement, /*incremental*/ false); + statements = insertStatementAfterCustomPrologue(statements.slice(), requireStatement); + } + else { + // Do nothing (script file) - consider an error in the checker? + } } } if (statements !== visited.statements) { @@ -306,14 +315,21 @@ namespace ts { } } + const callee = currentFileState.importSpecifier === undefined + ? createJsxFactoryExpression( + factory, + context.getEmitResolver().getJsxFactoryEntity(currentSourceFile), + compilerOptions.reactNamespace!, // TODO: GH#18217 + node + ) + : getImplicitImportForName("createElement"); + const element = createExpressionForJsxElement( factory, - context.getEmitResolver().getJsxFactoryEntity(currentSourceFile), - compilerOptions.reactNamespace!, // TODO: GH#18217 + callee, tagName, objectProperties, mapDefined(children, transformJsxChildToExpression), - node, location ); diff --git a/tests/baselines/reference/jsxJsxsCjsTransformKeyProp(jsx=react-jsx).js b/tests/baselines/reference/jsxJsxsCjsTransformKeyProp(jsx=react-jsx).js index 5cd85c1dfdd..4ef773b9111 100644 --- a/tests/baselines/reference/jsxJsxsCjsTransformKeyProp(jsx=react-jsx).js +++ b/tests/baselines/reference/jsxJsxsCjsTransformKeyProp(jsx=react-jsx).js @@ -21,8 +21,9 @@ var __assign = (this && this.__assign) || function () { return __assign.apply(this, arguments); }; exports.__esModule = true; +var react_1 = require("react"); var jsx_runtime_js_1 = require("react/jsx-runtime.js"); /// var props = { answer: 42 }; var a = jsx_runtime_js_1.jsx("div", __assign({}, props, { children: "text" }), "foo"); -var b = React.createElement("div", __assign({}, props, { key: "bar" }), "text"); +var b = react_1.createElement("div", __assign({}, props, { key: "bar" }), "text"); diff --git a/tests/baselines/reference/jsxJsxsCjsTransformKeyProp(jsx=react-jsxdev).js b/tests/baselines/reference/jsxJsxsCjsTransformKeyProp(jsx=react-jsxdev).js index 3a06a71cfcc..fa5bdb38552 100644 --- a/tests/baselines/reference/jsxJsxsCjsTransformKeyProp(jsx=react-jsxdev).js +++ b/tests/baselines/reference/jsxJsxsCjsTransformKeyProp(jsx=react-jsxdev).js @@ -21,9 +21,10 @@ var __assign = (this && this.__assign) || function () { return __assign.apply(this, arguments); }; exports.__esModule = true; +var react_1 = require("react"); var jsx_dev_runtime_js_1 = require("react/jsx-dev-runtime.js"); var _jsxFileName = "tests/cases/conformance/jsx/jsxs/jsxJsxsCjsTransformKeyProp.tsx"; /// var props = { answer: 42 }; var a = jsx_dev_runtime_js_1.jsxDEV("div", __assign({}, props, { children: "text" }), "foo", false, { fileName: _jsxFileName, lineNumber: 3, columnNumber: 10 }, this); -var b = React.createElement("div", __assign({}, props, { key: "bar" }), "text"); +var b = react_1.createElement("div", __assign({}, props, { key: "bar" }), "text"); diff --git a/tests/baselines/reference/jsxJsxsCjsTransformKeyPropCustomImport(jsx=react-jsx).js b/tests/baselines/reference/jsxJsxsCjsTransformKeyPropCustomImport(jsx=react-jsx).js index e1bcb7caf43..da3771ec312 100644 --- a/tests/baselines/reference/jsxJsxsCjsTransformKeyPropCustomImport(jsx=react-jsx).js +++ b/tests/baselines/reference/jsxJsxsCjsTransformKeyPropCustomImport(jsx=react-jsx).js @@ -21,8 +21,9 @@ var __assign = (this && this.__assign) || function () { return __assign.apply(this, arguments); }; exports.__esModule = true; +var preact_1 = require("preact"); var jsx_runtime_js_1 = require("preact/jsx-runtime.js"); /// var props = { answer: 42 }; var a = jsx_runtime_js_1.jsx("div", __assign({}, props, { children: "text" }), "foo"); -var b = React.createElement("div", __assign({}, props, { key: "bar" }), "text"); +var b = preact_1.createElement("div", __assign({}, props, { key: "bar" }), "text"); diff --git a/tests/baselines/reference/jsxJsxsCjsTransformKeyPropCustomImport(jsx=react-jsxdev).js b/tests/baselines/reference/jsxJsxsCjsTransformKeyPropCustomImport(jsx=react-jsxdev).js index b67bee03f45..46cbac3c9fd 100644 --- a/tests/baselines/reference/jsxJsxsCjsTransformKeyPropCustomImport(jsx=react-jsxdev).js +++ b/tests/baselines/reference/jsxJsxsCjsTransformKeyPropCustomImport(jsx=react-jsxdev).js @@ -21,9 +21,10 @@ var __assign = (this && this.__assign) || function () { return __assign.apply(this, arguments); }; exports.__esModule = true; +var preact_1 = require("preact"); var jsx_dev_runtime_js_1 = require("preact/jsx-dev-runtime.js"); var _jsxFileName = "tests/cases/conformance/jsx/jsxs/jsxJsxsCjsTransformKeyPropCustomImport.tsx"; /// var props = { answer: 42 }; var a = jsx_dev_runtime_js_1.jsxDEV("div", __assign({}, props, { children: "text" }), "foo", false, { fileName: _jsxFileName, lineNumber: 3, columnNumber: 10 }, this); -var b = React.createElement("div", __assign({}, props, { key: "bar" }), "text"); +var b = preact_1.createElement("div", __assign({}, props, { key: "bar" }), "text"); diff --git a/tests/baselines/reference/jsxJsxsCjsTransformKeyPropCustomImportPragma(jsx=react-jsx).js b/tests/baselines/reference/jsxJsxsCjsTransformKeyPropCustomImportPragma(jsx=react-jsx).js index 8af6119b2d6..11600352cd2 100644 --- a/tests/baselines/reference/jsxJsxsCjsTransformKeyPropCustomImportPragma(jsx=react-jsx).js +++ b/tests/baselines/reference/jsxJsxsCjsTransformKeyPropCustomImportPragma(jsx=react-jsx).js @@ -34,12 +34,13 @@ var __assign = (this && this.__assign) || function () { return __assign.apply(this, arguments); }; exports.__esModule = true; +var preact_1 = require("preact"); var jsx_runtime_js_1 = require("preact/jsx-runtime.js"); /// /* @jsxImportSource preact */ var props = { answer: 42 }; var a = jsx_runtime_js_1.jsx("div", __assign({}, props, { children: "text" }), "foo"); -var b = React.createElement("div", __assign({}, props, { key: "bar" }), "text"); +var b = preact_1.createElement("div", __assign({}, props, { key: "bar" }), "text"); //// [react.js] "use strict"; var __assign = (this && this.__assign) || function () { @@ -54,10 +55,11 @@ var __assign = (this && this.__assign) || function () { return __assign.apply(this, arguments); }; exports.__esModule = true; +var react_1 = require("react"); var jsx_runtime_js_1 = require("react/jsx-runtime.js"); /// /* @jsxImportSource react */ require("./preact"); var props2 = { answer: 42 }; var a2 = jsx_runtime_js_1.jsx("div", __assign({}, props2, { children: "text" }), "foo"); -var b2 = React.createElement("div", __assign({}, props2, { key: "bar" }), "text"); +var b2 = react_1.createElement("div", __assign({}, props2, { key: "bar" }), "text"); diff --git a/tests/baselines/reference/jsxJsxsCjsTransformKeyPropCustomImportPragma(jsx=react-jsxdev).js b/tests/baselines/reference/jsxJsxsCjsTransformKeyPropCustomImportPragma(jsx=react-jsxdev).js index f4ccb5d7307..672fe8faa10 100644 --- a/tests/baselines/reference/jsxJsxsCjsTransformKeyPropCustomImportPragma(jsx=react-jsxdev).js +++ b/tests/baselines/reference/jsxJsxsCjsTransformKeyPropCustomImportPragma(jsx=react-jsxdev).js @@ -34,13 +34,14 @@ var __assign = (this && this.__assign) || function () { return __assign.apply(this, arguments); }; exports.__esModule = true; +var preact_1 = require("preact"); var jsx_dev_runtime_js_1 = require("preact/jsx-dev-runtime.js"); var _jsxFileName = "tests/cases/conformance/jsx/jsxs/preact.tsx"; /// /* @jsxImportSource preact */ var props = { answer: 42 }; var a = jsx_dev_runtime_js_1.jsxDEV("div", __assign({}, props, { children: "text" }), "foo", false, { fileName: _jsxFileName, lineNumber: 4, columnNumber: 10 }, this); -var b = React.createElement("div", __assign({}, props, { key: "bar" }), "text"); +var b = preact_1.createElement("div", __assign({}, props, { key: "bar" }), "text"); //// [react.js] "use strict"; var __assign = (this && this.__assign) || function () { @@ -55,6 +56,7 @@ var __assign = (this && this.__assign) || function () { return __assign.apply(this, arguments); }; exports.__esModule = true; +var react_1 = require("react"); var jsx_dev_runtime_js_1 = require("react/jsx-dev-runtime.js"); var _jsxFileName = "tests/cases/conformance/jsx/jsxs/react.tsx"; /// @@ -62,4 +64,4 @@ var _jsxFileName = "tests/cases/conformance/jsx/jsxs/react.tsx"; require("./preact"); var props2 = { answer: 42 }; var a2 = jsx_dev_runtime_js_1.jsxDEV("div", __assign({}, props2, { children: "text" }), "foo", false, { fileName: _jsxFileName, lineNumber: 5, columnNumber: 11 }, this); -var b2 = React.createElement("div", __assign({}, props2, { key: "bar" }), "text"); +var b2 = react_1.createElement("div", __assign({}, props2, { key: "bar" }), "text");