namespace ts . refactor { const refactorName = "Convert import" ; const namespaceToNamedAction = { name : "Convert namespace import to named imports" , description : Diagnostics . Convert_namespace_import_to_named_imports . message , kind : "refactor.rewrite.import.named" , } ; const namedToNamespaceAction = { name : "Convert named imports to namespace import" , description : Diagnostics . Convert_named_imports_to_namespace_import . message , kind : "refactor.rewrite.import.namespace" , } ; registerRefactor ( refactorName , { kinds : [ namespaceToNamedAction . kind , namedToNamespaceAction . kind ] , getAvailableActions : function getRefactorActionsToConvertBetweenNamedAndNamespacedImports ( context ) : readonly ApplicableRefactorInfo [ ] { const info = getImportToConvert ( context , context . triggerReason === "invoked" ) ; if ( ! info ) return emptyArray ; if ( ! isRefactorErrorInfo ( info ) ) { const namespaceImport = info . kind === SyntaxKind . NamespaceImport ; const action = namespaceImport ? namespaceToNamedAction : namedToNamespaceAction ; return [ { name : refactorName , description : action . description , actions : [ action ] } ] ; } if ( context . preferences . provideRefactorNotApplicableReason ) { return [ { name : refactorName , description : namespaceToNamedAction . description , actions : [ { ... namespaceToNamedAction , notApplicableReason : info . error } ] } , { name : refactorName , description : namedToNamespaceAction . description , actions : [ { ... namedToNamespaceAction , notApplicableReason : info . error } ] } ] ; } return emptyArray ; } , getEditsForAction : function getRefactorEditsToConvertBetweenNamedAndNamespacedImports ( context , actionName ) : RefactorEditInfo { Debug . assert ( actionName === namespaceToNamedAction . name || actionName === namedToNamespaceAction . name , "Unexpected action name" ) ; const info = getImportToConvert ( context ) ; Debug . assert ( info && ! isRefactorErrorInfo ( info ) , "Expected applicable refactor info" ) ; const edits = textChanges . ChangeTracker . with ( context , t => doChange ( context . file , context . program , t , info ) ) ; return { edits , renameFilename : undefined , renameLocation : undefined } ; } } ) ; function getImportToConvert ( context , considerPartialSpans = true ) { const { file } = context ; const span = getRefactorContextSpan ( context ) ; const token = getTokenAtPosition ( file , span . start ) ; const importDecl = considerPartialSpans ? findAncestor ( token , isImportDeclaration ) : getParentNodeInSpan ( token , file , span ) ; if ( ! importDecl || ! isImportDeclaration ( importDecl ) ) return { error : "Selection is not an import declaration." } ; const end = span . start + span . length ; const nextToken = findNextToken ( importDecl , importDecl . parent , file ) ; if ( nextToken && end > nextToken . getStart ( ) ) return undefined ; const { importClause } = importDecl ; if ( ! importClause ) { return { error : getLocaleSpecificMessage ( Diagnostics . Could_not_find_import_clause ) } ; } if ( ! importClause . namedBindings ) { return { error : getLocaleSpecificMessage ( Diagnostics . Could_not_find_namespace_import_or_named_imports ) } ; } return importClause . namedBindings ; } function doChange ( sourceFile , program , changes , toConvert ) { const checker = program . getTypeChecker ( ) ; if ( toConvert . kind === SyntaxKind . NamespaceImport ) { doChangeNamespaceToNamed ( sourceFile , checker , changes , toConvert , getAllowSyntheticDefaultImports ( program . getCompilerOptions ( ) ) ) ; } else { doChangeNamedToNamespace ( sourceFile , checker , changes , toConvert ) ; } } function doChangeNamespaceToNamed ( sourceFile , checker , changes , toConvert , allowSyntheticDefaultImports ) { let usedAsNamespaceOrDefault = false ; const nodesToReplace = [ ] ; const conflictingNames = new Map < string , true > ( ) ; FindAllReferences . Core . eachSymbolReferenceInFile ( toConvert . name , checker , sourceFile , id => { if ( ! isPropertyAccessOrQualifiedName ( id . parent ) ) { usedAsNamespaceOrDefault = true ; } else { const exportName = getRightOfPropertyAccessOrQualifiedName ( id . parent ) . text ; if ( checker . resolveName ( exportName , id , SymbolFlags . All , true ) ) { conflictingNames . set ( exportName , true ) ; } Debug . assert ( getLeftOfPropertyAccessOrQualifiedName ( id . parent ) === id , "Parent expression should match id" ) ; nodesToReplace . push ( id . parent ) ; } } ) ; const exportNameToImportName = new Map < string , string > ( ) ; for ( const propertyAccessOrQualifiedName of nodesToReplace ) { const exportName = getRightOfPropertyAccessOrQualifiedName ( propertyAccessOrQualifiedName ) . text ; let importName = exportNameToImportName . get ( exportName ) ; if ( importName === undefined ) { exportNameToImportName . set ( exportName , importName = conflictingNames . has ( exportName ) ? getUniqueName ( exportName , sourceFile ) : exportName ) ; } changes . replaceNode ( sourceFile , propertyAccessOrQualifiedName , factory . createIdentifier ( importName ) ) ; } const importSpecifiers = [ ] ; exportNameToImportName . forEach ( ( name , propertyName ) => { importSpecifiers . push ( factory . createImportSpecifier ( false , name === propertyName ? undefined : factory . createIdentifier ( propertyName ) , factory . createIdentifier ( name ) ) ) ; } ) ; const importDecl = toConvert . parent . parent ; if ( usedAsNamespaceOrDefault && ! allowSyntheticDefaultImports ) { changes . insertNodeAfter ( sourceFile , importDecl , updateImport ( importDecl , undefined , importSpecifiers ) ) ; } else { changes . replaceNode ( sourceFile , importDecl , updateImport ( importDecl , usedAsNamespaceOrDefault ? factory . createIdentifier ( toConvert . name . text ) : undefined , importSpecifiers ) ) ; } } function getRightOfPropertyAccessOrQualifiedName ( propertyAccessOrQualifiedName ) { return isPropertyAccessExpression ( propertyAccessOrQualifiedName ) ? propertyAccessOrQualifiedName . name : propertyAccessOrQualifiedName . right ; } function getLeftOfPropertyAccessOrQualifiedName ( propertyAccessOrQualifiedName ) { return isPropertyAccessExpression ( propertyAccessOrQualifiedName ) ? propertyAccessOrQualifiedName . expression : propertyAccessOrQualifiedName . left ; } function doChangeNamedToNamespace ( sourceFile , checker , changes , toConvert ) { const importDecl = toConvert . parent . parent ; const { moduleSpecifier } = importDecl ; const toConvertSymbols = new Set ( ) ; toConvert . elements . forEach ( namedImport => { const symbol = checker . getSymbolAtLocation ( namedImport . name ) ; if ( symbol ) { toConvertSymbols . add ( symbol ) ; } } ) ; const preferredName = moduleSpecifier && isStringLiteral ( moduleSpecifier ) ? codefix . moduleSpecifierToValidIdentifier ( moduleSpecifier . text , ScriptTarget . ESNext ) : "module" ; function hasNamespaceNameConflict ( namedImport ) { return ! ! FindAllReferences . Core . eachSymbolReferenceInFile ( namedImport . name , checker , sourceFile , id => { const symbol = checker . resolveName ( preferredName , id , SymbolFlags . All , true ) ; if ( symbol ) { if ( toConvertSymbols . has ( symbol ) ) { return isExportSpecifier ( id . parent ) ; } return true ; } return false ; } ) ; } const namespaceNameConflicts = toConvert . elements . some ( hasNamespaceNameConflict ) ; const namespaceImportName = namespaceNameConflicts ? getUniqueName ( preferredName , sourceFile ) : preferredName ; const neededNamedImports = new Set ( ) ; for ( const element of toConvert . elements ) { const propertyName = ( element . propertyName || element . name ) . text ; FindAllReferences . Core . eachSymbolReferenceInFile ( element . name , checker , sourceFile , id => { const access = factory . createPropertyAccessExpression ( factory . createIdentifier ( namespaceImportName ) , propertyName ) ; if ( isShorthandPropertyAssignment ( id . parent ) ) { changes . replaceNode ( sourceFile , id . parent , factory . createPropertyAssignment ( id . text , access ) ) ; } else if ( isExportSpecifier ( id . parent ) ) { neededNamedImports . add ( element ) ; } else { changes . replaceNode ( sourceFile , id , access ) ; } } ) ; } changes . replaceNode ( sourceFile , toConvert , factory . createNamespaceImport ( factory . createIdentifier ( namespaceImportName ) ) ) ; if ( neededNamedImports . size ) { const newNamedImports = arrayFrom ( neededNamedImports . values ( ) ) . map ( element => factory . createImportSpecifier ( element . isTypeOnly , element . propertyName && factory . createIdentifier ( element . propertyName . text ) , factory . createIdentifier ( element . name . text ) ) ) ; changes . insertNodeAfter ( sourceFile , toConvert . parent . parent , updateImport ( importDecl , undefined , newNamedImports ) ) ; } } function updateImport ( old , defaultImportName , elements ) { return factory . createImportDeclaration ( undefined , undefined , factory . createImportClause ( false , defaultImportName , elements && elements . length ? factory . createNamedImports ( elements ) : undefined ) , old . moduleSpecifier , undefined ) ; } }