importFixes: Distinguish when we need to import JSX constructor or JSX namespace (#22828)

This commit is contained in:
Andy 2018-03-26 14:05:03 -07:00 committed by GitHub
parent c9ac15ae56
commit ced4c00522
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 38 additions and 11 deletions

View File

@ -702,18 +702,18 @@ namespace ts.codefix {
}
}
function getActionsForNonUMDImport(context: CodeFixContext): CodeAction[] {
function getActionsForNonUMDImport(context: CodeFixContext): CodeAction[] | undefined {
// This will always be an Identifier, since the diagnostics we fix only fail on identifiers.
const { sourceFile, span, program, cancellationToken } = context;
const checker = program.getTypeChecker();
const symbolToken = getTokenAtPosition(sourceFile, span.start, /*includeJsDocComment*/ false);
const isJsxNamespace = isJsxOpeningLikeElement(symbolToken.parent) && symbolToken.parent.tagName === symbolToken;
if (!isJsxNamespace && !isIdentifier(symbolToken)) {
return undefined;
}
const symbolName = isJsxNamespace ? checker.getJsxNamespace() : (<Identifier>symbolToken).text;
const allSourceFiles = program.getSourceFiles();
const compilerOptions = program.getCompilerOptions();
// If we're at `<Foo/>`, we must check if `Foo` is already in scope, and if so, get an import for `React` instead.
const symbolName = isJsxOpeningLikeElement(symbolToken.parent)
&& symbolToken.parent.tagName === symbolToken
&& (!isIdentifier(symbolToken) || isIntrinsicJsxName(symbolToken.text) || checker.resolveName(symbolToken.text, symbolToken, SymbolFlags.All, /*excludeGlobals*/ false))
? checker.getJsxNamespace()
: isIdentifier(symbolToken) ? symbolToken.text : undefined;
if (!symbolName) return undefined;
// "default" is a keyword and not a legal identifier for the import, so we don't expect it here
Debug.assert(symbolName !== "default");
@ -725,7 +725,7 @@ namespace ts.codefix {
function addSymbol(moduleSymbol: Symbol, exportedSymbol: Symbol, importKind: ImportKind): void {
originalSymbolToExportInfos.add(getUniqueSymbolId(exportedSymbol, checker).toString(), { moduleSymbol, importKind });
}
forEachExternalModuleToImportFrom(checker, sourceFile, allSourceFiles, moduleSymbol => {
forEachExternalModuleToImportFrom(checker, sourceFile, program.getSourceFiles(), moduleSymbol => {
cancellationToken.throwIfCancellationRequested();
// check the default export
@ -735,7 +735,7 @@ namespace ts.codefix {
if ((
localSymbol && localSymbol.escapedName === symbolName ||
getEscapedNameForExportDefault(defaultExport) === symbolName ||
moduleSymbolToValidIdentifier(moduleSymbol, compilerOptions.target) === symbolName
moduleSymbolToValidIdentifier(moduleSymbol, program.getCompilerOptions().target) === symbolName
) && checkSymbolHasMeaning(localSymbol || defaultExport, currentTokenMeaning)) {
addSymbol(moduleSymbol, localSymbol || defaultExport, ImportKind.Default);
}

View File

@ -2,9 +2,36 @@
// @jsx: react
// @Filename: /node_modules/react/index.d.ts
////export const React: any;
// @Filename: /a.tsx
////[|<this>|]</this>
// Tests that we don't crash at non-identifier location.
// @Filename: /Foo.tsx
////export const Foo = 0;
// @Filename: /c.tsx
////import { React } from "react";
////[|<Foo />;|]
// @Filename: /d.tsx
////[|import { Foo } from "./Foo";
////<Foo />;|]
// Tests that we don't crash at non-identifier location.
goTo.file("/a.tsx");
verify.importFixAtPosition([]);
// When constructor is missing, provide fix for that
goTo.file("/c.tsx");
verify.importFixAtPosition([
`import { Foo } from "./Foo";
<Foo />;`]);
// When JSX namespace is missing, provide fix for that
goTo.file("/d.tsx");
verify.importFixAtPosition([
`import { Foo } from "./Foo";
import { React } from "react";
<Foo />;`]);