Skip to content

Commit

Permalink
fix(33511): show jsx namespace default import quick fix if it does no…
Browse files Browse the repository at this point in the history
…t exists in the current scope
  • Loading branch information
a-tarasyuk committed May 8, 2020
1 parent 7ac641d commit 594c628
Show file tree
Hide file tree
Showing 7 changed files with 176 additions and 6 deletions.
18 changes: 12 additions & 6 deletions src/services/codefixes/importFixes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 `<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
&& (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");

Expand All @@ -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,
Expand Down
31 changes: 31 additions & 0 deletions tests/cases/fourslash/importNameCodeFix_jsx2.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/// <reference path="fourslash.ts" />

// @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|]></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";
<Text></Text>;`
});
31 changes: 31 additions & 0 deletions tests/cases/fourslash/importNameCodeFix_jsx3.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/// <reference path="fourslash.ts" />

// @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></[|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";
<Text></Text>;`
});
31 changes: 31 additions & 0 deletions tests/cases/fourslash/importNameCodeFix_jsx4.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/// <reference path="fourslash.ts" />

// @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";
////<Text></Text>;

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";
<Text></Text>;`
});
31 changes: 31 additions & 0 deletions tests/cases/fourslash/importNameCodeFix_jsx5.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/// <reference path="fourslash.ts" />

// @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";
<Text />;`
});
40 changes: 40 additions & 0 deletions tests/cases/fourslash/importNameCodeFix_jsx6.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/// <reference path="fourslash.ts" />

// @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|]></Text>;

goTo.file("/a.tsx");
verify.codeFix({
index: 0,
applyChanges: true,
description: `Import 'Text' from module "react-native"`,
newFileContent:
`import { Text } from "react-native";
<Text></Text>;`
});

verify.codeFix({
description: `Import default 'React' from module "react"`,
newFileContent:
`import { Text } from "react-native";
import React from "react";
<Text></Text>;`
});

0 comments on commit 594c628

Please sign in to comment.