Skip to content

Commit 95b1330

Browse files
committed
fix(33511): show jsx namespace default import quick fix if it does not exists in the current scope
1 parent a88957e commit 95b1330

8 files changed

+192
-6
lines changed

src/services/codefixes/importFixes.ts

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -485,12 +485,7 @@ namespace ts.codefix {
485485

486486
function getFixesInfoForNonUMDImport({ sourceFile, program, cancellationToken, host, preferences }: CodeFixContextBase, symbolToken: Identifier): FixesInfo | undefined {
487487
const checker = program.getTypeChecker();
488-
// If we're at `<Foo/>`, we must check if `Foo` is already in scope, and if so, get an import for `React` instead.
489-
const symbolName = isJsxOpeningLikeElement(symbolToken.parent)
490-
&& symbolToken.parent.tagName === symbolToken
491-
&& (isIntrinsicJsxName(symbolToken.text) || checker.resolveName(symbolToken.text, symbolToken, SymbolFlags.All, /*excludeGlobals*/ false))
492-
? checker.getJsxNamespace(sourceFile)
493-
: symbolToken.text;
488+
const symbolName = getSymbolName(sourceFile, checker, symbolToken);
494489
// "default" is a keyword and not a legal identifier for the import, so we don't expect it here
495490
Debug.assert(symbolName !== InternalSymbolName.Default, "'default' isn't a legal identifier and couldn't occur here");
496491

@@ -503,6 +498,17 @@ namespace ts.codefix {
503498
return { fixes, symbolName };
504499
}
505500

501+
function getSymbolName(sourceFile: SourceFile, checker: TypeChecker, symbolToken: Identifier): string {
502+
const parent = symbolToken.parent;
503+
if ((isJsxOpeningLikeElement(parent) || isJsxClosingElement(parent)) && parent.tagName === symbolToken) {
504+
const jsxNamespace = checker.getJsxNamespace(sourceFile);
505+
if (isIntrinsicJsxName(symbolToken.text) || !checker.resolveName(jsxNamespace, parent, SymbolFlags.All, /*excludeGlobals*/ true)) {
506+
return jsxNamespace;
507+
}
508+
}
509+
return symbolToken.text;
510+
}
511+
506512
// Returns a map from an exported symbol's ID to a list of every way it's (re-)exported.
507513
function getExportInfos(
508514
symbolName: string,
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/// <reference path="fourslash.ts" />
2+
3+
// @jsx: react
4+
// @module: esnext
5+
// @esModuleInterop: true
6+
// @moduleResolution: node
7+
8+
// @Filename: /node_modules/react/index.d.ts
9+
////export = React;
10+
////export as namespace React;
11+
////declare namespace React {
12+
//// class Component {}
13+
////}
14+
15+
// @Filename: /node_modules/react-native/index.d.ts
16+
////import * as React from "react";
17+
////export class Text extends React.Component {};
18+
19+
// @Filename: /a.tsx
20+
////import React from "react";
21+
////<[|Text|]></Text>;
22+
23+
goTo.file("/a.tsx");
24+
verify.codeFix({
25+
index: 0,
26+
description: `Import 'Text' from module "react-native"`,
27+
newFileContent:
28+
`import React from "react";
29+
import { Text } from "react-native";
30+
<Text></Text>;`
31+
});
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/// <reference path="fourslash.ts" />
2+
3+
// @jsx: react
4+
// @module: esnext
5+
// @esModuleInterop: true
6+
// @moduleResolution: node
7+
8+
// @Filename: /node_modules/react/index.d.ts
9+
////export = React;
10+
////export as namespace React;
11+
////declare namespace React {
12+
//// class Component {}
13+
////}
14+
15+
// @Filename: /node_modules/react-native/index.d.ts
16+
////import * as React from "react";
17+
////export class Text extends React.Component {};
18+
19+
// @Filename: /a.tsx
20+
////import React from "react";
21+
////<Text></[|Text|]>;
22+
23+
goTo.file("/a.tsx");
24+
verify.codeFix({
25+
index: 0,
26+
description: `Import 'Text' from module "react-native"`,
27+
newFileContent:
28+
`import React from "react";
29+
import { Text } from "react-native";
30+
<Text></Text>;`
31+
});
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/// <reference path="fourslash.ts" />
2+
3+
// @jsx: react
4+
// @module: esnext
5+
// @esModuleInterop: true
6+
// @moduleResolution: node
7+
8+
// @Filename: /node_modules/react/index.d.ts
9+
////export = React;
10+
////export as namespace React;
11+
////declare namespace React {
12+
//// class Component {}
13+
////}
14+
15+
// @Filename: /node_modules/react-native/index.d.ts
16+
////import * as React from "react";
17+
////export class Text extends React.Component {};
18+
19+
// @Filename: /a.tsx
20+
////import { Text } from "react-native";
21+
////<Text></Text>;
22+
23+
goTo.file("/a.tsx");
24+
verify.codeFix({
25+
index: 0,
26+
description: `Import default 'React' from module "react"`,
27+
newFileContent:
28+
`import { Text } from "react-native";
29+
import React from "react";
30+
<Text></Text>;`
31+
});
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/// <reference path="fourslash.ts" />
2+
3+
// @jsx: react
4+
// @module: esnext
5+
// @esModuleInterop: true
6+
// @moduleResolution: node
7+
8+
// @Filename: /node_modules/react/index.d.ts
9+
////export = React;
10+
////export as namespace React;
11+
////declare namespace React {
12+
//// class Component {}
13+
////}
14+
15+
// @Filename: /node_modules/react-native/index.d.ts
16+
////import * as React from "react";
17+
////export class Text extends React.Component {};
18+
19+
// @Filename: /a.tsx
20+
////import React from "react";
21+
////<[|Text|] />;
22+
23+
goTo.file("/a.tsx");
24+
verify.codeFix({
25+
index: 0,
26+
description: `Import 'Text' from module "react-native"`,
27+
newFileContent:
28+
`import React from "react";
29+
import { Text } from "react-native";
30+
<Text />;`
31+
});
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/// <reference path="fourslash.ts" />
2+
3+
// @jsx: react
4+
// @module: esnext
5+
// @esModuleInterop: true
6+
// @moduleResolution: node
7+
8+
// @Filename: /node_modules/react/index.d.ts
9+
////export = React;
10+
////export as namespace React;
11+
////declare namespace React {
12+
//// class Component {}
13+
////}
14+
15+
// @Filename: /node_modules/react-native/index.d.ts
16+
////import * as React from "react";
17+
////export class Text extends React.Component {};
18+
19+
// @Filename: /a.tsx
20+
////<[|Text|]></Text>;
21+
22+
goTo.file("/a.tsx");
23+
verify.codeFix({
24+
index: 0,
25+
description: `Import default 'React' from module "react"`,
26+
applyChanges: true,
27+
newFileContent:
28+
`import React from "react";
29+
30+
<Text></Text>;`
31+
});
32+
33+
verify.codeFix({
34+
index: 0,
35+
description: `Import 'Text' from module "react-native"`,
36+
newFileContent:
37+
`import React from "react";
38+
import { Text } from "react-native";
39+
40+
<Text></Text>;`
41+
});
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/// <reference path="fourslash.ts" />
2+
3+
// @jsx: react
4+
// @module: esnext
5+
// @esModuleInterop: true
6+
// @moduleResolution: node
7+
8+
// @Filename: /node_modules/react/index.d.ts
9+
////// React was not defined
10+
11+
// @Filename: /a.tsx
12+
////<[|Text|]></Text>;
13+
14+
goTo.file("/a.tsx");
15+
verify.not.codeFixAvailable();

0 commit comments

Comments
 (0)