diff --git a/src/services/codefixes/importFixes.ts b/src/services/codefixes/importFixes.ts index 060d409eaa9e1..757624cee7445 100644 --- a/src/services/codefixes/importFixes.ts +++ b/src/services/codefixes/importFixes.ts @@ -485,12 +485,7 @@ namespace ts.codefix { function getFixesInfoForNonUMDImport({ sourceFile, program, cancellationToken, host, preferences }: CodeFixContextBase, symbolToken: Identifier): 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"); @@ -503,6 +498,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 (!checker.resolveName(jsxNamespace, parent, SymbolFlags.All, /*excludeGlobals*/ false)) { + 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..170c405f7b3bb --- /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: `Import 'Text' from module "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..754f624d14b1f --- /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: `Import 'Text' from module "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..ab2a423acb02c --- /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: `Import default 'React' from module "react"`, + newFileContent: +`import { Text } from "react-native"; +import React from "react"; +;` +}); diff --git a/tests/cases/fourslash/importNameCodeFix_jsx5.ts b/tests/cases/fourslash/importNameCodeFix_jsx5.ts new file mode 100644 index 0000000000000..be853204c2db0 --- /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: `Import 'Text' from module "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..a2a607e06b13e --- /dev/null +++ b/tests/cases/fourslash/importNameCodeFix_jsx6.ts @@ -0,0 +1,40 @@ +/// + +// @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, + applyChanges: true, + description: `Import 'Text' from module "react-native"`, + newFileContent: +`import { Text } from "react-native"; + +;` +}); + +verify.codeFix({ + description: `Import default 'React' from module "react"`, + newFileContent: +`import { Text } from "react-native"; +import React from "react"; + +;` +});