diff --git a/src/services/codefixes/importFixes.ts b/src/services/codefixes/importFixes.ts index 8e411e1be757e..15c7f9a17fce8 100644 --- a/src/services/codefixes/importFixes.ts +++ b/src/services/codefixes/importFixes.ts @@ -491,12 +491,7 @@ namespace ts.codefix { function getFixesInfoForNonUMDImport({ sourceFile, program, cancellationToken, host, preferences }: CodeFixContextBase, symbolToken: Identifier, useAutoImportProvider: boolean): FixesInfo | undefined { const checker = program.getTypeChecker(); - // If we're at ``, 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 - && (isIntrinsicJsxName(symbolToken.text) || checker.resolveName(symbolToken.text, symbolToken, SymbolFlags.All, /*excludeGlobals*/ false)) - ? checker.getJsxNamespace(sourceFile) - : symbolToken.text; + const symbolName = getSymbolName(sourceFile, checker, symbolToken); // "default" is a keyword and not a legal identifier for the import, so we don't expect it here Debug.assert(symbolName !== InternalSymbolName.Default, "'default' isn't a legal identifier and couldn't occur here"); @@ -509,6 +504,17 @@ namespace ts.codefix { return { fixes, symbolName }; } + function getSymbolName(sourceFile: SourceFile, checker: TypeChecker, symbolToken: Identifier): string { + const parent = symbolToken.parent; + if ((isJsxOpeningLikeElement(parent) || isJsxClosingElement(parent)) && parent.tagName === symbolToken) { + const jsxNamespace = checker.getJsxNamespace(sourceFile); + if (isIntrinsicJsxName(symbolToken.text) || !checker.resolveName(jsxNamespace, parent, SymbolFlags.Value, /*excludeGlobals*/ true)) { + return jsxNamespace; + } + } + return symbolToken.text; + } + // Returns a map from an exported symbol's ID to a list of every way it's (re-)exported. function getExportInfos( symbolName: string, diff --git a/tests/cases/fourslash/importNameCodeFix_jsx.ts b/tests/cases/fourslash/importNameCodeFix_jsx1.ts similarity index 100% rename from tests/cases/fourslash/importNameCodeFix_jsx.ts rename to tests/cases/fourslash/importNameCodeFix_jsx1.ts diff --git a/tests/cases/fourslash/importNameCodeFix_jsx2.ts b/tests/cases/fourslash/importNameCodeFix_jsx2.ts new file mode 100644 index 0000000000000..f740876ed7ec3 --- /dev/null +++ b/tests/cases/fourslash/importNameCodeFix_jsx2.ts @@ -0,0 +1,31 @@ +/// + +// @jsx: react +// @module: esnext +// @esModuleInterop: true +// @moduleResolution: node + +// @Filename: /node_modules/react/index.d.ts +////export = React; +////export as namespace React; +////declare namespace React { +//// class Component {} +////} + +// @Filename: /node_modules/react-native/index.d.ts +////import * as React from "react"; +////export class Text extends React.Component {}; + +// @Filename: /a.tsx +////import React from "react"; +////<[|Text|]>; + +goTo.file("/a.tsx"); +verify.codeFix({ + index: 0, + description: [ts.Diagnostics.Import_0_from_module_1.message, "Text", "react-native"], + newFileContent: +`import React from "react"; +import { Text } from "react-native"; +;` +}); diff --git a/tests/cases/fourslash/importNameCodeFix_jsx3.ts b/tests/cases/fourslash/importNameCodeFix_jsx3.ts new file mode 100644 index 0000000000000..f889076734823 --- /dev/null +++ b/tests/cases/fourslash/importNameCodeFix_jsx3.ts @@ -0,0 +1,31 @@ +/// + +// @jsx: react +// @module: esnext +// @esModuleInterop: true +// @moduleResolution: node + +// @Filename: /node_modules/react/index.d.ts +////export = React; +////export as namespace React; +////declare namespace React { +//// class Component {} +////} + +// @Filename: /node_modules/react-native/index.d.ts +////import * as React from "react"; +////export class Text extends React.Component {}; + +// @Filename: /a.tsx +////import React from "react"; +////; + +goTo.file("/a.tsx"); +verify.codeFix({ + index: 0, + description: [ts.Diagnostics.Import_0_from_module_1.message, "Text", "react-native"], + newFileContent: +`import React from "react"; +import { Text } from "react-native"; +;` +}); diff --git a/tests/cases/fourslash/importNameCodeFix_jsx4.ts b/tests/cases/fourslash/importNameCodeFix_jsx4.ts new file mode 100644 index 0000000000000..2303336d83cb2 --- /dev/null +++ b/tests/cases/fourslash/importNameCodeFix_jsx4.ts @@ -0,0 +1,31 @@ +/// + +// @jsx: react +// @module: esnext +// @esModuleInterop: true +// @moduleResolution: node + +// @Filename: /node_modules/react/index.d.ts +////export = React; +////export as namespace React; +////declare namespace React { +//// class Component {} +////} + +// @Filename: /node_modules/react-native/index.d.ts +////import * as React from "react"; +////export class Text extends React.Component {}; + +// @Filename: /a.tsx +////import { Text } from "react-native"; +////; + +goTo.file("/a.tsx"); +verify.codeFix({ + index: 0, + description: [ts.Diagnostics.Import_default_0_from_module_1.message, "React", "react"], + newFileContent: +`import React from "react"; +import { Text } from "react-native"; +;` +}); diff --git a/tests/cases/fourslash/importNameCodeFix_jsx5.ts b/tests/cases/fourslash/importNameCodeFix_jsx5.ts new file mode 100644 index 0000000000000..510175f375f82 --- /dev/null +++ b/tests/cases/fourslash/importNameCodeFix_jsx5.ts @@ -0,0 +1,31 @@ +/// + +// @jsx: react +// @module: esnext +// @esModuleInterop: true +// @moduleResolution: node + +// @Filename: /node_modules/react/index.d.ts +////export = React; +////export as namespace React; +////declare namespace React { +//// class Component {} +////} + +// @Filename: /node_modules/react-native/index.d.ts +////import * as React from "react"; +////export class Text extends React.Component {}; + +// @Filename: /a.tsx +////import React from "react"; +////<[|Text|] />; + +goTo.file("/a.tsx"); +verify.codeFix({ + index: 0, + description: [ts.Diagnostics.Import_0_from_module_1.message, "Text", "react-native"], + newFileContent: +`import React from "react"; +import { Text } from "react-native"; +;` +}); diff --git a/tests/cases/fourslash/importNameCodeFix_jsx6.ts b/tests/cases/fourslash/importNameCodeFix_jsx6.ts new file mode 100644 index 0000000000000..d98117fae67cf --- /dev/null +++ b/tests/cases/fourslash/importNameCodeFix_jsx6.ts @@ -0,0 +1,41 @@ +/// + +// @jsx: react +// @module: esnext +// @esModuleInterop: true +// @moduleResolution: node + +// @Filename: /node_modules/react/index.d.ts +////export = React; +////export as namespace React; +////declare namespace React { +//// class Component {} +////} + +// @Filename: /node_modules/react-native/index.d.ts +////import * as React from "react"; +////export class Text extends React.Component {}; + +// @Filename: /a.tsx +////<[|Text|]>; + +goTo.file("/a.tsx"); +verify.codeFix({ + index: 0, + description: [ts.Diagnostics.Import_default_0_from_module_1.message, "React", "react"], + applyChanges: true, + newFileContent: +`import React from "react"; + +;` +}); + +verify.codeFix({ + index: 0, + description: [ts.Diagnostics.Import_0_from_module_1.message, "Text", "react-native"], + newFileContent: +`import React from "react"; +import { Text } from "react-native"; + +;` +}); diff --git a/tests/cases/fourslash/importNameCodeFix_jsx7.ts b/tests/cases/fourslash/importNameCodeFix_jsx7.ts new file mode 100644 index 0000000000000..545d7db76e798 --- /dev/null +++ b/tests/cases/fourslash/importNameCodeFix_jsx7.ts @@ -0,0 +1,15 @@ +/// + +// @jsx: react +// @module: esnext +// @esModuleInterop: true +// @moduleResolution: node + +// @Filename: /node_modules/react/index.d.ts +////// React was not defined + +// @Filename: /a.tsx +////<[|Text|]>; + +goTo.file("/a.tsx"); +verify.not.codeFixAvailable();