Skip to content

Commit 4150a46

Browse files
authored
* Add `-dev` to version patch number in master * Refactor to handel non-identifier tokens for UMD modules * Be more graceful handeling non-identifier tokens in import fixes * Fix bad merge * Remove check for isIdentifier
1 parent 2a17825 commit 4150a46

4 files changed

Lines changed: 76 additions & 34 deletions

File tree

src/compiler/checker.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2464,10 +2464,6 @@ namespace ts {
24642464
(ignoreQualification || canQualifySymbol(symbolFromSymbolTable, meaning));
24652465
}
24662466

2467-
function isUMDExportSymbol(symbol: Symbol) {
2468-
return symbol && symbol.declarations && symbol.declarations[0] && isNamespaceExportDeclaration(symbol.declarations[0]);
2469-
}
2470-
24712467
function trySymbolTable(symbols: SymbolTable, ignoreQualification: boolean | undefined) {
24722468
// If symbol is directly available by its name in the symbol table
24732469
if (isAccessible(symbols.get(symbol.escapedName), /*resolvedAliasSymbol*/ undefined, ignoreQualification)) {

src/compiler/utilities.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3770,6 +3770,10 @@ namespace ts {
37703770
export function forSomeAncestorDirectory(directory: string, callback: (directory: string) => boolean): boolean {
37713771
return !!forEachAncestorDirectory(directory, d => callback(d) ? true : undefined);
37723772
}
3773+
3774+
export function isUMDExportSymbol(symbol: Symbol) {
3775+
return symbol && symbol.declarations && symbol.declarations[0] && isNamespaceExportDeclaration(symbol.declarations[0]);
3776+
}
37733777
}
37743778

37753779
namespace ts {

src/services/codefixes/importFixes.ts

Lines changed: 59 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -38,27 +38,22 @@ namespace ts.codefix {
3838
return { description, changes, fixId: undefined };
3939
}
4040

41-
function convertToImportCodeFixContext(context: CodeFixContext): { context: ImportCodeFixContext, isJsxNamespace: boolean } {
41+
function convertToImportCodeFixContext(context: CodeFixContext, symbolToken: Node, symbolName: string): ImportCodeFixContext {
4242
const useCaseSensitiveFileNames = context.host.useCaseSensitiveFileNames ? context.host.useCaseSensitiveFileNames() : false;
4343
const { program } = context;
4444
const checker = program.getTypeChecker();
4545

46-
const symbolToken = getTokenAtPosition(context.sourceFile, context.span.start, /*includeJsDocComment*/ false);
47-
const isJsxNamespace = isJsxOpeningLikeElement(symbolToken.parent) && symbolToken.parent.tagName === symbolToken;
4846
return {
49-
context: {
50-
host: context.host,
51-
formatContext: context.formatContext,
52-
sourceFile: context.sourceFile,
53-
program,
54-
checker,
55-
compilerOptions: program.getCompilerOptions(),
56-
cachedImportDeclarations: [],
57-
getCanonicalFileName: createGetCanonicalFileName(useCaseSensitiveFileNames),
58-
symbolName: isJsxNamespace ? checker.getJsxNamespace() : cast(symbolToken, isIdentifier).text,
59-
symbolToken,
60-
},
61-
isJsxNamespace,
47+
host: context.host,
48+
formatContext: context.formatContext,
49+
sourceFile: context.sourceFile,
50+
program,
51+
checker,
52+
compilerOptions: program.getCompilerOptions(),
53+
cachedImportDeclarations: [],
54+
getCanonicalFileName: createGetCanonicalFileName(useCaseSensitiveFileNames),
55+
symbolName,
56+
symbolToken
6257
};
6358
}
6459

@@ -647,19 +642,43 @@ namespace ts.codefix {
647642
}
648643

649644
function getImportCodeActions(context: CodeFixContext): CodeAction[] {
650-
const { context: importFixContext, isJsxNamespace } = convertToImportCodeFixContext(context);
651645
return context.errorCode === Diagnostics._0_refers_to_a_UMD_global_but_the_current_file_is_a_module_Consider_adding_an_import_instead.code
652-
? getActionsForUMDImport(importFixContext, isJsxNamespace)
653-
: getActionsForNonUMDImport(importFixContext, context.program.getSourceFiles(), context.cancellationToken);
646+
? getActionsForUMDImport(context)
647+
: getActionsForNonUMDImport(context);
654648
}
655649

656-
function getActionsForUMDImport(context: ImportCodeFixContext, isJsxNamespace: boolean): CodeAction[] {
657-
const { checker, symbolName, symbolToken, compilerOptions } = context;
658-
const symbol = skipAlias(
659-
isJsxNamespace ? checker.resolveName(symbolName, /*location*/ undefined, SymbolFlags.Value, /*excludeGlobals*/ false) : checker.getSymbolAtLocation(symbolToken),
660-
checker);
661-
return getCodeActionsForImport([{ moduleSymbol: symbol, importKind: getUmdImportKind(compilerOptions) }], { ...context });
650+
function getActionsForUMDImport(context: CodeFixContext): CodeAction[] {
651+
const token = getTokenAtPosition(context.sourceFile, context.span.start, /*includeJsDocComment*/ false);
652+
const checker = context.program.getTypeChecker();
653+
654+
let umdSymbol: Symbol | undefined;
655+
656+
if (isIdentifier(token)) {
657+
// try the identifier to see if it is the umd symbol
658+
umdSymbol = checker.getSymbolAtLocation(token);
659+
}
660+
661+
if (!isUMDExportSymbol(umdSymbol)) {
662+
// The error wasn't for the symbolAtLocation, it was for the JSX tag itself, which needs access to e.g. `React`.
663+
const parent = token.parent;
664+
const isNodeOpeningLikeElement = isJsxOpeningLikeElement(parent);
665+
if ((isJsxOpeningLikeElement && (<JsxOpeningLikeElement>parent).tagName === token) || parent.kind === SyntaxKind.JsxOpeningFragment) {
666+
umdSymbol = checker.resolveName(checker.getJsxNamespace(),
667+
isNodeOpeningLikeElement ? (<JsxOpeningLikeElement>parent).tagName : parent, SymbolFlags.Value, /*excludeGlobals*/ false);
668+
}
669+
}
670+
671+
if (isUMDExportSymbol(umdSymbol)) {
672+
const symbol = checker.getAliasedSymbol(umdSymbol);
673+
if (symbol) {
674+
return getCodeActionsForImport([{ moduleSymbol: symbol, importKind: getUmdImportKind(context.program.getCompilerOptions()) }],
675+
convertToImportCodeFixContext(context, token, umdSymbol.name));
676+
}
677+
}
678+
679+
return undefined;
662680
}
681+
663682
function getUmdImportKind(compilerOptions: CompilerOptions) {
664683
// Import a synthetic `default` if enabled.
665684
if (getAllowSyntheticDefaultImports(compilerOptions)) {
@@ -684,8 +703,19 @@ namespace ts.codefix {
684703
}
685704
}
686705

687-
function getActionsForNonUMDImport(context: ImportCodeFixContext, allSourceFiles: ReadonlyArray<SourceFile>, cancellationToken: CancellationToken): CodeAction[] {
688-
const { sourceFile, checker, symbolName, symbolToken } = context;
706+
function getActionsForNonUMDImport(context: CodeFixContext): CodeAction[] {
707+
// This will always be an Identifier, since the diagnostics we fix only fail on identifiers.
708+
const { sourceFile, span, program, cancellationToken } = context;
709+
const checker = program.getTypeChecker();
710+
const symbolToken = getTokenAtPosition(sourceFile, span.start, /*includeJsDocComment*/ false);
711+
const isJsxNamespace = isJsxOpeningLikeElement(symbolToken.parent) && symbolToken.parent.tagName === symbolToken;
712+
if (!isJsxNamespace && !isIdentifier(symbolToken)) {
713+
return undefined;
714+
}
715+
const symbolName = isJsxNamespace ? checker.getJsxNamespace() : (<Identifier>symbolToken).text;
716+
const allSourceFiles = program.getSourceFiles();
717+
const compilerOptions = program.getCompilerOptions();
718+
689719
// "default" is a keyword and not a legal identifier for the import, so we don't expect it here
690720
Debug.assert(symbolName !== "default");
691721
const currentTokenMeaning = getMeaningFromLocation(symbolToken);
@@ -706,7 +736,7 @@ namespace ts.codefix {
706736
if ((
707737
localSymbol && localSymbol.escapedName === symbolName ||
708738
getEscapedNameForExportDefault(defaultExport) === symbolName ||
709-
moduleSymbolToValidIdentifier(moduleSymbol, context.compilerOptions.target) === symbolName
739+
moduleSymbolToValidIdentifier(moduleSymbol, compilerOptions.target) === symbolName
710740
) && checkSymbolHasMeaning(localSymbol || defaultExport, currentTokenMeaning)) {
711741
addSymbol(moduleSymbol, localSymbol || defaultExport, ImportKind.Default);
712742
}
@@ -735,7 +765,7 @@ namespace ts.codefix {
735765
}
736766
});
737767

738-
return arrayFrom(flatMapIterator(originalSymbolToExportInfos.values(), exportInfos => getCodeActionsForImport(exportInfos, context)));
768+
return arrayFrom(flatMapIterator(originalSymbolToExportInfos.values(), exportInfos => getCodeActionsForImport(exportInfos, convertToImportCodeFixContext(context, symbolToken, symbolName))));
739769
}
740770

741771
function checkSymbolHasMeaning({ declarations }: Symbol, meaning: SemanticMeaning): boolean {

tests/cases/fourslash/importNameCodeFixUMDGlobalReact0.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,22 @@
2222
////export class MyMap extends Component { }
2323
////<MyMap/>;|]
2424

25+
// @Filename: /b.tsx
26+
////[|import { Component } from "react";
27+
////<></>;|]
28+
2529
goTo.file("/a.tsx");
2630

2731
verify.importFixAtPosition([
28-
`import { Component } from "react";
32+
`import { Component } from "react";
2933
import * as React from "react";
3034
export class MyMap extends Component { }
3135
<MyMap/>;`]);
36+
37+
38+
goTo.file("/b.tsx");
39+
40+
verify.importFixAtPosition([
41+
`import { Component } from "react";
42+
import * as React from "react";
43+
<></>;`]);

0 commit comments

Comments
 (0)