diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 80b61edd3657a..37d8fc4725c93 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -640,6 +640,7 @@ import { isJsxCallLike, isJsxElement, isJsxFragment, + isJsxIntrinsicTagName, isJsxNamespacedName, isJsxOpeningElement, isJsxOpeningFragment, @@ -833,7 +834,6 @@ import { JsxExpression, JsxFlags, JsxFragment, - JsxNamespacedName, JsxOpeningElement, JsxOpeningFragment, JsxOpeningLikeElement, @@ -33099,13 +33099,6 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return (name as string).includes("-"); } - /** - * Returns true iff React would emit this tag name as a string rather than an identifier or qualified name - */ - function isJsxIntrinsicTagName(tagName: Node): tagName is Identifier | JsxNamespacedName { - return isIdentifier(tagName) && isIntrinsicJsxName(tagName.escapedText) || isJsxNamespacedName(tagName); - } - function checkJsxAttribute(node: JsxAttribute, checkMode?: CheckMode) { return node.initializer ? checkExpressionForMutableLocation(node.initializer, checkMode) diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index 75656cf5aefab..5ba6307851bcb 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -103,6 +103,8 @@ import { isInstantiatedModule, isJsonSourceFile, isJsxAttributes, + isJsxIntrinsicTagName, + isJSXTagName, isJsxTagNameExpression, isLeftHandSideExpression, isLocalName, @@ -2688,8 +2690,10 @@ export function transformTypeScript(context: TransformationContext): Transformer // an identifier that is exported from a merged namespace. const container = resolver.getReferencedExportContainer(node, /*prefixLocals*/ false); if (container && container.kind !== SyntaxKind.SourceFile) { - const substitute = (applicableSubstitutions & TypeScriptSubstitutionFlags.NamespaceExports && container.kind === SyntaxKind.ModuleDeclaration) || - (applicableSubstitutions & TypeScriptSubstitutionFlags.NonQualifiedEnumMembers && container.kind === SyntaxKind.EnumDeclaration); + let originalNode; + const substitute = ((applicableSubstitutions & TypeScriptSubstitutionFlags.NamespaceExports && container.kind === SyntaxKind.ModuleDeclaration) || + (applicableSubstitutions & TypeScriptSubstitutionFlags.NonQualifiedEnumMembers && container.kind === SyntaxKind.EnumDeclaration)) && + (!isJSXTagName(originalNode = getOriginalNode(node, isIdentifier)) || !isJsxIntrinsicTagName(originalNode)); if (substitute) { return setTextRange( factory.createPropertyAccessExpression(factory.getGeneratedNameForNode(container), node), diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 1ff3a2acada46..3af5393760c2d 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -3549,6 +3549,14 @@ export function isJSXTagName(node: Node): boolean { return false; } +/** + * @internal + * Returns true if React would emit this tag name as a string rather than an identifier or qualified name + */ +export function isJsxIntrinsicTagName(tagName: Node): tagName is Identifier | JsxNamespacedName { + return isIdentifier(tagName) && isIntrinsicJsxName(tagName.escapedText) || isJsxNamespacedName(tagName); +} + /** @internal */ export function isExpressionNode(node: Node): boolean { switch (node.kind) { diff --git a/tests/baselines/reference/namespaceJsxPreserve1.js b/tests/baselines/reference/namespaceJsxPreserve1.js new file mode 100644 index 0000000000000..d9152e1f9de25 --- /dev/null +++ b/tests/baselines/reference/namespaceJsxPreserve1.js @@ -0,0 +1,27 @@ +//// [tests/cases/compiler/namespaceJsxPreserve1.tsx] //// + +//// [namespaceJsxPreserve1.tsx] +/// + +export namespace form { + export const input = null; + + export const HiddenInput = () => null; + + export const test1 = ; + export const test2 = ; +} + + +//// [namespaceJsxPreserve1.jsx] +"use strict"; +/// +Object.defineProperty(exports, "__esModule", { value: true }); +exports.form = void 0; +var form; +(function (form) { + form.input = null; + form.HiddenInput = function () { return null; }; + form.test1 = ; + form.test2 = ; +})(form || (exports.form = form = {})); diff --git a/tests/baselines/reference/namespaceJsxPreserve1.symbols b/tests/baselines/reference/namespaceJsxPreserve1.symbols new file mode 100644 index 0000000000000..e842bbd616796 --- /dev/null +++ b/tests/baselines/reference/namespaceJsxPreserve1.symbols @@ -0,0 +1,23 @@ +//// [tests/cases/compiler/namespaceJsxPreserve1.tsx] //// + +=== namespaceJsxPreserve1.tsx === +/// + +export namespace form { +>form : Symbol(form, Decl(namespaceJsxPreserve1.tsx, 0, 0)) + + export const input = null; +>input : Symbol(input, Decl(namespaceJsxPreserve1.tsx, 3, 14)) + + export const HiddenInput = () => null; +>HiddenInput : Symbol(HiddenInput, Decl(namespaceJsxPreserve1.tsx, 5, 14)) + + export const test1 = ; +>test1 : Symbol(test1, Decl(namespaceJsxPreserve1.tsx, 7, 14)) +>input : Symbol(JSX.IntrinsicElements.input, Decl(react16.d.ts, 2570, 106)) + + export const test2 = ; +>test2 : Symbol(test2, Decl(namespaceJsxPreserve1.tsx, 8, 14)) +>HiddenInput : Symbol(HiddenInput, Decl(namespaceJsxPreserve1.tsx, 5, 14)) +} + diff --git a/tests/baselines/reference/namespaceJsxPreserve1.types b/tests/baselines/reference/namespaceJsxPreserve1.types new file mode 100644 index 0000000000000..17153fffad9cc --- /dev/null +++ b/tests/baselines/reference/namespaceJsxPreserve1.types @@ -0,0 +1,42 @@ +//// [tests/cases/compiler/namespaceJsxPreserve1.tsx] //// + +=== Performance Stats === +Assignability cache: 2,500 +Type Count: 10,000 +Instantiation count: 100,000 +Symbol count: 50,000 + +=== namespaceJsxPreserve1.tsx === +/// + +export namespace form { +>form : typeof form +> : ^^^^^^^^^^^ + + export const input = null; +>input : null +> : ^^^^ + + export const HiddenInput = () => null; +>HiddenInput : () => null +> : ^^^^^^^^^^ +>() => null : () => null +> : ^^^^^^^^^^ + + export const test1 = ; +>test1 : JSX.Element +> : ^^^^^^^^^^^ +> : JSX.Element +> : ^^^^^^^^^^^ +>input : null +> : ^^^^ + + export const test2 = ; +>test2 : JSX.Element +> : ^^^^^^^^^^^ +> : JSX.Element +> : ^^^^^^^^^^^ +>HiddenInput : () => null +> : ^^^^^^^^^^ +} + diff --git a/tests/cases/compiler/namespaceJsxPreserve1.tsx b/tests/cases/compiler/namespaceJsxPreserve1.tsx new file mode 100644 index 0000000000000..ec5cc3d1e334d --- /dev/null +++ b/tests/cases/compiler/namespaceJsxPreserve1.tsx @@ -0,0 +1,13 @@ +// @strict: true +// @jsx: preserve + +/// + +export namespace form { + export const input = null; + + export const HiddenInput = () => null; + + export const test1 = ; + export const test2 = ; +}