Skip to content

Commit

Permalink
Merge pull request #41151 from Andarist/fix/create-element-new-jsx-tr…
Browse files Browse the repository at this point in the history
…ansform

Fix/create element new jsx transform
  • Loading branch information
weswigham committed Oct 20, 2020
2 parents c5cbd52 + 0ad22de commit 8b728c8
Show file tree
Hide file tree
Showing 23 changed files with 734 additions and 32 deletions.
6 changes: 3 additions & 3 deletions src/compiler/factory/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -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);
Expand All @@ -88,7 +88,7 @@ namespace ts {

return setTextRange(
factory.createCallExpression(
createJsxFactoryExpression(factory, jsxFactoryEntity, reactNamespace, parentElement),
callee,
/*typeArguments*/ undefined,
argumentsList
),
Expand Down
74 changes: 45 additions & 29 deletions src/compiler/transformers/jsx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ namespace ts {
interface PerFileState {
importSpecifier?: string;
filenameDeclaration?: VariableDeclaration & { name: Identifier; };
utilizedImplicitRuntimeImports?: Map<ImportSpecifier>;
utilizedImplicitRuntimeImports?: Map<Map<ImportSpecifier>>;
}

const {
Expand Down Expand Up @@ -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" : "jsx-runtime"}`;
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;
}

Expand All @@ -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" : "jsx-runtime"}`;
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) {
Expand Down Expand Up @@ -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
);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
//// [jsxJsxsCjsTransformKeyProp.tsx]
/// <reference path="/.lib/react16.d.ts" />
const props = { answer: 42 }
const a = <div key="foo" {...props}>text</div>;
const b = <div {...props} key="bar">text</div>;

export {};


//// [jsxJsxsCjsTransformKeyProp.js]
"use strict";
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
exports.__esModule = true;
var react_1 = require("react");
var jsx_runtime_1 = require("react/jsx-runtime");
/// <reference path="react16.d.ts" />
var props = { answer: 42 };
var a = jsx_runtime_1.jsx("div", __assign({}, props, { children: "text" }), "foo");
var b = react_1.createElement("div", __assign({}, props, { key: "bar" }), "text");
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
=== tests/cases/conformance/jsx/jsxs/jsxJsxsCjsTransformKeyProp.tsx ===
/// <reference path="react16.d.ts" />
const props = { answer: 42 }
>props : Symbol(props, Decl(jsxJsxsCjsTransformKeyProp.tsx, 1, 5))
>answer : Symbol(answer, Decl(jsxJsxsCjsTransformKeyProp.tsx, 1, 15))

const a = <div key="foo" {...props}>text</div>;
>a : Symbol(a, Decl(jsxJsxsCjsTransformKeyProp.tsx, 2, 5))
>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2420, 114))
>key : Symbol(key, Decl(jsxJsxsCjsTransformKeyProp.tsx, 2, 14))
>props : Symbol(props, Decl(jsxJsxsCjsTransformKeyProp.tsx, 1, 5))
>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2420, 114))

const b = <div {...props} key="bar">text</div>;
>b : Symbol(b, Decl(jsxJsxsCjsTransformKeyProp.tsx, 3, 5))
>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2420, 114))
>props : Symbol(props, Decl(jsxJsxsCjsTransformKeyProp.tsx, 1, 5))
>key : Symbol(key, Decl(jsxJsxsCjsTransformKeyProp.tsx, 3, 25))
>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2420, 114))

export {};

Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
=== tests/cases/conformance/jsx/jsxs/jsxJsxsCjsTransformKeyProp.tsx ===
/// <reference path="react16.d.ts" />
const props = { answer: 42 }
>props : { answer: number; }
>{ answer: 42 } : { answer: number; }
>answer : number
>42 : 42

const a = <div key="foo" {...props}>text</div>;
>a : JSX.Element
><div key="foo" {...props}>text</div> : JSX.Element
>div : any
>key : string
>props : { answer: number; }
>div : any

const b = <div {...props} key="bar">text</div>;
>b : JSX.Element
><div {...props} key="bar">text</div> : JSX.Element
>div : any
>props : { answer: number; }
>key : string
>div : any

export {};

Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//// [jsxJsxsCjsTransformKeyProp.tsx]
/// <reference path="/.lib/react16.d.ts" />
const props = { answer: 42 }
const a = <div key="foo" {...props}>text</div>;
const b = <div {...props} key="bar">text</div>;

export {};


//// [jsxJsxsCjsTransformKeyProp.js]
"use strict";
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
exports.__esModule = true;
var react_1 = require("react");
var jsx_dev_runtime_1 = require("react/jsx-dev-runtime");
var _jsxFileName = "tests/cases/conformance/jsx/jsxs/jsxJsxsCjsTransformKeyProp.tsx";
/// <reference path="react16.d.ts" />
var props = { answer: 42 };
var a = jsx_dev_runtime_1.jsxDEV("div", __assign({}, props, { children: "text" }), "foo", false, { fileName: _jsxFileName, lineNumber: 3, columnNumber: 10 }, this);
var b = react_1.createElement("div", __assign({}, props, { key: "bar" }), "text");
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
=== tests/cases/conformance/jsx/jsxs/jsxJsxsCjsTransformKeyProp.tsx ===
/// <reference path="react16.d.ts" />
const props = { answer: 42 }
>props : Symbol(props, Decl(jsxJsxsCjsTransformKeyProp.tsx, 1, 5))
>answer : Symbol(answer, Decl(jsxJsxsCjsTransformKeyProp.tsx, 1, 15))

const a = <div key="foo" {...props}>text</div>;
>a : Symbol(a, Decl(jsxJsxsCjsTransformKeyProp.tsx, 2, 5))
>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2420, 114))
>key : Symbol(key, Decl(jsxJsxsCjsTransformKeyProp.tsx, 2, 14))
>props : Symbol(props, Decl(jsxJsxsCjsTransformKeyProp.tsx, 1, 5))
>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2420, 114))

const b = <div {...props} key="bar">text</div>;
>b : Symbol(b, Decl(jsxJsxsCjsTransformKeyProp.tsx, 3, 5))
>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2420, 114))
>props : Symbol(props, Decl(jsxJsxsCjsTransformKeyProp.tsx, 1, 5))
>key : Symbol(key, Decl(jsxJsxsCjsTransformKeyProp.tsx, 3, 25))
>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2420, 114))

export {};

Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
=== tests/cases/conformance/jsx/jsxs/jsxJsxsCjsTransformKeyProp.tsx ===
/// <reference path="react16.d.ts" />
const props = { answer: 42 }
>props : { answer: number; }
>{ answer: 42 } : { answer: number; }
>answer : number
>42 : 42

const a = <div key="foo" {...props}>text</div>;
>a : JSX.Element
><div key="foo" {...props}>text</div> : JSX.Element
>div : any
>key : string
>props : { answer: number; }
>div : any

const b = <div {...props} key="bar">text</div>;
>b : JSX.Element
><div {...props} key="bar">text</div> : JSX.Element
>div : any
>props : { answer: number; }
>key : string
>div : any

export {};

Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
//// [jsxJsxsCjsTransformKeyPropCustomImport.tsx]
/// <reference path="/.lib/react16.d.ts" />
const props = { answer: 42 }
const a = <div key="foo" {...props}>text</div>;
const b = <div {...props} key="bar">text</div>;

export {};


//// [jsxJsxsCjsTransformKeyPropCustomImport.js]
"use strict";
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
exports.__esModule = true;
var preact_1 = require("preact");
var jsx_runtime_1 = require("preact/jsx-runtime");
/// <reference path="react16.d.ts" />
var props = { answer: 42 };
var a = jsx_runtime_1.jsx("div", __assign({}, props, { children: "text" }), "foo");
var b = preact_1.createElement("div", __assign({}, props, { key: "bar" }), "text");
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
=== tests/cases/conformance/jsx/jsxs/jsxJsxsCjsTransformKeyPropCustomImport.tsx ===
/// <reference path="react16.d.ts" />
const props = { answer: 42 }
>props : Symbol(props, Decl(jsxJsxsCjsTransformKeyPropCustomImport.tsx, 1, 5))
>answer : Symbol(answer, Decl(jsxJsxsCjsTransformKeyPropCustomImport.tsx, 1, 15))

const a = <div key="foo" {...props}>text</div>;
>a : Symbol(a, Decl(jsxJsxsCjsTransformKeyPropCustomImport.tsx, 2, 5))
>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2420, 114))
>key : Symbol(key, Decl(jsxJsxsCjsTransformKeyPropCustomImport.tsx, 2, 14))
>props : Symbol(props, Decl(jsxJsxsCjsTransformKeyPropCustomImport.tsx, 1, 5))
>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2420, 114))

const b = <div {...props} key="bar">text</div>;
>b : Symbol(b, Decl(jsxJsxsCjsTransformKeyPropCustomImport.tsx, 3, 5))
>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2420, 114))
>props : Symbol(props, Decl(jsxJsxsCjsTransformKeyPropCustomImport.tsx, 1, 5))
>key : Symbol(key, Decl(jsxJsxsCjsTransformKeyPropCustomImport.tsx, 3, 25))
>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2420, 114))

export {};

Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
=== tests/cases/conformance/jsx/jsxs/jsxJsxsCjsTransformKeyPropCustomImport.tsx ===
/// <reference path="react16.d.ts" />
const props = { answer: 42 }
>props : { answer: number; }
>{ answer: 42 } : { answer: number; }
>answer : number
>42 : 42

const a = <div key="foo" {...props}>text</div>;
>a : JSX.Element
><div key="foo" {...props}>text</div> : JSX.Element
>div : any
>key : string
>props : { answer: number; }
>div : any

const b = <div {...props} key="bar">text</div>;
>b : JSX.Element
><div {...props} key="bar">text</div> : JSX.Element
>div : any
>props : { answer: number; }
>key : string
>div : any

export {};

0 comments on commit 8b728c8

Please sign in to comment.