From 437588b89c59a0d6313201d4c0651183fdc0b61e Mon Sep 17 00:00:00 2001 From: Alexander T Date: Mon, 13 Jul 2020 07:52:23 +0300 Subject: [PATCH] fix(33511): show jsx namespace default import quick fix if it does not exists in the current scope --- src/services/codefixes/importFixes.ts | 18 +++++--- ...deFix_jsx.ts => importNameCodeFix_jsx1.ts} | 0 .../cases/fourslash/importNameCodeFix_jsx2.ts | 31 ++++++++++++++ .../cases/fourslash/importNameCodeFix_jsx3.ts | 31 ++++++++++++++ .../cases/fourslash/importNameCodeFix_jsx4.ts | 31 ++++++++++++++ .../cases/fourslash/importNameCodeFix_jsx5.ts | 31 ++++++++++++++ .../cases/fourslash/importNameCodeFix_jsx6.ts | 41 +++++++++++++++++++ .../cases/fourslash/importNameCodeFix_jsx7.ts | 15 +++++++ 8 files changed, 192 insertions(+), 6 deletions(-) rename tests/cases/fourslash/{importNameCodeFix_jsx.ts => importNameCodeFix_jsx1.ts} (100%) create mode 100644 tests/cases/fourslash/importNameCodeFix_jsx2.ts create mode 100644 tests/cases/fourslash/importNameCodeFix_jsx3.ts create mode 100644 tests/cases/fourslash/importNameCodeFix_jsx4.ts create mode 100644 tests/cases/fourslash/importNameCodeFix_jsx5.ts create mode 100644 tests/cases/fourslash/importNameCodeFix_jsx6.ts create mode 100644 tests/cases/fourslash/importNameCodeFix_jsx7.ts 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();