diff --git a/packages/core/integration-tests/test/ts-types.js b/packages/core/integration-tests/test/ts-types.js index c09d989dc9e..9d26b4cab47 100644 --- a/packages/core/integration-tests/test/ts-types.js +++ b/packages/core/integration-tests/test/ts-types.js @@ -466,6 +466,7 @@ describe('typescript types', function () { index.ts: export * from "./ErrorBoundary"; export * from "./ErrorBoundaryContext"; + export * from "./Component"; foo.js: import {baz} from './baz'; @@ -495,6 +496,11 @@ describe('typescript types', function () { ); } } + + Component.tsx: + export function Foo() { + return

Foo

; + } `; let b = await bundle(path.join(dir, '/index.ts'), { @@ -505,12 +511,13 @@ describe('typescript types', function () { let output = await outputFS.readFile(b.getBundles()[0].filePath, 'utf8'); assert.equal( output, - `import { Context, Component, PropsWithChildren, ProviderProps, FunctionComponentElement } from "react"; + `import { Context, Component, PropsWithChildren, ProviderProps, FunctionComponentElement, JSX } from "react"; export type ErrorBoundaryContextType = {}; export const ErrorBoundaryContext: Context; export class ErrorBoundary extends Component { render(): FunctionComponentElement>; } +export function Foo(): JSX.Element; //# sourceMappingURL=types.d.ts.map `, diff --git a/packages/transformers/typescript-types/src/collect.js b/packages/transformers/typescript-types/src/collect.js index eddf82f3338..5beaab0d143 100644 --- a/packages/transformers/typescript-types/src/collect.js +++ b/packages/transformers/typescript-types/src/collect.js @@ -2,7 +2,7 @@ import type {TSModuleGraph} from './TSModuleGraph'; import nullthrows from 'nullthrows'; -import ts from 'typescript'; +import ts, {type EntityName} from 'typescript'; import {TSModule} from './TSModule'; import {getExportedName, isDeclaration} from './utils'; @@ -91,16 +91,9 @@ export function collect( ts.isStringLiteral(node.argument.literal) ) { let local = `$$parcel$import$${moduleGraph.syntheticImportCount++}`; - if (node.qualifier) { - currentModule.addImport( - local, - node.argument.literal.text, - node.qualifier.text, - ); - } else { - currentModule.addImport(local, node.argument.literal.text, '*'); - } - return factory.createTypeReferenceNode(local, node.typeArguments); + let [specifier, entity] = getImportName(node.qualifier, local, factory); + currentModule.addImport(local, node.argument.literal.text, specifier); + return factory.createTypeReferenceNode(entity, node.typeArguments); } // Handle `export default name;` @@ -142,3 +135,23 @@ export function collect( return ts.visitNode(sourceFile, visit); } + +// Traverse down an EntityName to the root identifier. Return that to use as the named import specifier, +// and collect the remaining parts into a new QualifiedName with the local replacement at the root. +// import('react').JSX.Element => import {JSX} from 'react'; JSX.Element +function getImportName( + qualifier: ?EntityName, + local: string, + factory: typeof ts, +) { + if (!qualifier) { + return ['*', factory.createIdentifier(local)]; + } + + if (qualifier.kind === ts.SyntaxKind.Identifier) { + return [qualifier.text, factory.createIdentifier(local)]; + } + + let [name, entity] = getImportName(qualifier.left, local, factory); + return [name, factory.createQualifiedName(entity, qualifier.right)]; +}