Skip to content

Commit ced4c00

Browse files
author
Andy
authored
importFixes: Distinguish when we need to import JSX constructor or JSX namespace (microsoft#22828)
1 parent c9ac15a commit ced4c00

File tree

2 files changed

+38
-11
lines changed

2 files changed

+38
-11
lines changed

src/services/codefixes/importFixes.ts

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -702,18 +702,18 @@ namespace ts.codefix {
702702
}
703703
}
704704

705-
function getActionsForNonUMDImport(context: CodeFixContext): CodeAction[] {
705+
function getActionsForNonUMDImport(context: CodeFixContext): CodeAction[] | undefined {
706706
// This will always be an Identifier, since the diagnostics we fix only fail on identifiers.
707707
const { sourceFile, span, program, cancellationToken } = context;
708708
const checker = program.getTypeChecker();
709709
const symbolToken = getTokenAtPosition(sourceFile, span.start, /*includeJsDocComment*/ false);
710-
const isJsxNamespace = isJsxOpeningLikeElement(symbolToken.parent) && symbolToken.parent.tagName === symbolToken;
711-
if (!isJsxNamespace && !isIdentifier(symbolToken)) {
712-
return undefined;
713-
}
714-
const symbolName = isJsxNamespace ? checker.getJsxNamespace() : (<Identifier>symbolToken).text;
715-
const allSourceFiles = program.getSourceFiles();
716-
const compilerOptions = program.getCompilerOptions();
710+
// If we're at `<Foo/>`, we must check if `Foo` is already in scope, and if so, get an import for `React` instead.
711+
const symbolName = isJsxOpeningLikeElement(symbolToken.parent)
712+
&& symbolToken.parent.tagName === symbolToken
713+
&& (!isIdentifier(symbolToken) || isIntrinsicJsxName(symbolToken.text) || checker.resolveName(symbolToken.text, symbolToken, SymbolFlags.All, /*excludeGlobals*/ false))
714+
? checker.getJsxNamespace()
715+
: isIdentifier(symbolToken) ? symbolToken.text : undefined;
716+
if (!symbolName) return undefined;
717717

718718
// "default" is a keyword and not a legal identifier for the import, so we don't expect it here
719719
Debug.assert(symbolName !== "default");
@@ -725,7 +725,7 @@ namespace ts.codefix {
725725
function addSymbol(moduleSymbol: Symbol, exportedSymbol: Symbol, importKind: ImportKind): void {
726726
originalSymbolToExportInfos.add(getUniqueSymbolId(exportedSymbol, checker).toString(), { moduleSymbol, importKind });
727727
}
728-
forEachExternalModuleToImportFrom(checker, sourceFile, allSourceFiles, moduleSymbol => {
728+
forEachExternalModuleToImportFrom(checker, sourceFile, program.getSourceFiles(), moduleSymbol => {
729729
cancellationToken.throwIfCancellationRequested();
730730

731731
// check the default export
@@ -735,7 +735,7 @@ namespace ts.codefix {
735735
if ((
736736
localSymbol && localSymbol.escapedName === symbolName ||
737737
getEscapedNameForExportDefault(defaultExport) === symbolName ||
738-
moduleSymbolToValidIdentifier(moduleSymbol, compilerOptions.target) === symbolName
738+
moduleSymbolToValidIdentifier(moduleSymbol, program.getCompilerOptions().target) === symbolName
739739
) && checkSymbolHasMeaning(localSymbol || defaultExport, currentTokenMeaning)) {
740740
addSymbol(moduleSymbol, localSymbol || defaultExport, ImportKind.Default);
741741
}

tests/cases/fourslash/importNameCodeFix_jsx.ts

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,36 @@
22

33
// @jsx: react
44

5+
// @Filename: /node_modules/react/index.d.ts
6+
////export const React: any;
7+
58
// @Filename: /a.tsx
69
////[|<this>|]</this>
710

8-
// Tests that we don't crash at non-identifier location.
11+
// @Filename: /Foo.tsx
12+
////export const Foo = 0;
13+
14+
// @Filename: /c.tsx
15+
////import { React } from "react";
16+
////[|<Foo />;|]
17+
18+
// @Filename: /d.tsx
19+
////[|import { Foo } from "./Foo";
20+
////<Foo />;|]
921

22+
// Tests that we don't crash at non-identifier location.
23+
goTo.file("/a.tsx");
1024
verify.importFixAtPosition([]);
25+
26+
// When constructor is missing, provide fix for that
27+
goTo.file("/c.tsx");
28+
verify.importFixAtPosition([
29+
`import { Foo } from "./Foo";
30+
<Foo />;`]);
31+
32+
// When JSX namespace is missing, provide fix for that
33+
goTo.file("/d.tsx");
34+
verify.importFixAtPosition([
35+
`import { Foo } from "./Foo";
36+
import { React } from "react";
37+
<Foo />;`]);

0 commit comments

Comments
 (0)