@@ -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 {
0 commit comments