diff --git a/src/services/codefixes/useDefaultImport.ts b/src/services/codefixes/useDefaultImport.ts index a7efb9b98f33e..77b8bbde70e6e 100644 --- a/src/services/codefixes/useDefaultImport.ts +++ b/src/services/codefixes/useDefaultImport.ts @@ -7,8 +7,10 @@ import { AnyImportSyntax, Diagnostics, Expression, + factory, getQuotePreference, getTokenAtPosition, + hasSyntacticModifier, Identifier, isExternalModuleReference, isIdentifier, @@ -16,6 +18,7 @@ import { isImportEqualsDeclaration, isNamespaceImport, makeImport, + ModifierFlags, SourceFile, textChanges, UserPreferences, @@ -44,20 +47,34 @@ interface Info { readonly importNode: AnyImportSyntax; readonly name: Identifier; readonly moduleSpecifier: Expression; + readonly exportModifier: boolean; } function getInfo(sourceFile: SourceFile, pos: number): Info | undefined { const name = getTokenAtPosition(sourceFile, pos); if (!isIdentifier(name)) return undefined; // bad input const { parent } = name; if (isImportEqualsDeclaration(parent) && isExternalModuleReference(parent.moduleReference)) { - return { importNode: parent, name, moduleSpecifier: parent.moduleReference.expression }; + return { importNode: parent, name, moduleSpecifier: parent.moduleReference.expression, exportModifier: hasSyntacticModifier(parent, ModifierFlags.Export) }; } else if (isNamespaceImport(parent) && isImportDeclaration(parent.parent.parent)) { const importNode = parent.parent.parent; - return { importNode, name, moduleSpecifier: importNode.moduleSpecifier }; + return { importNode, name, moduleSpecifier: importNode.moduleSpecifier, exportModifier: false }; } } function doChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, info: Info, preferences: UserPreferences): void { changes.replaceNode(sourceFile, info.importNode, makeImport(info.name, /*namedImports*/ undefined, info.moduleSpecifier, getQuotePreference(sourceFile, preferences))); + if (info.exportModifier) { + changes.insertNodeAfter( + sourceFile, + info.importNode, + factory.createExportDeclaration( + /*modifiers*/ undefined, + /*isTypeOnly*/ false, + factory.createNamedExports([ + factory.createExportSpecifier(/*isTypeOnly*/ false, /*propertyName*/ undefined, info.name), + ]), + ), + ); + } } diff --git a/src/services/suggestionDiagnostics.ts b/src/services/suggestionDiagnostics.ts index 80fe528e791f1..3c4cf5c39b976 100644 --- a/src/services/suggestionDiagnostics.ts +++ b/src/services/suggestionDiagnostics.ts @@ -25,7 +25,6 @@ import { getFunctionFlags, hasInitializer, hasPropertyAccessExpressionWithName, - hasSyntacticModifier, Identifier, importFromModuleSpecifier, isAsyncFunction, @@ -37,7 +36,6 @@ import { isFunctionExpression, isFunctionLike, isIdentifier, - isImportEqualsDeclaration, isPropertyAccessExpression, isRequireCall, isReturnStatement, @@ -46,7 +44,6 @@ import { isVariableDeclaration, isVariableStatement, MethodDeclaration, - ModifierFlags, ModuleKind, Node, NodeFlags, @@ -88,7 +85,6 @@ export function computeSuggestionDiagnostics(sourceFile: SourceFile, program: Pr if (getAllowSyntheticDefaultImports(program.getCompilerOptions())) { for (const moduleSpecifier of sourceFile.imports) { const importNode = importFromModuleSpecifier(moduleSpecifier); - if (isImportEqualsDeclaration(importNode) && hasSyntacticModifier(importNode, ModifierFlags.Export)) continue; const name = importNameForConvertToDefaultImport(importNode); if (!name) continue; const module = program.getResolvedModuleFromModuleSpecifier(moduleSpecifier, sourceFile)?.resolvedModule; diff --git a/tests/cases/fourslash/codeFixUseDefaultImport.ts b/tests/cases/fourslash/codeFixUseDefaultImport.ts index 5521d7c8f5c62..c86ea2ce93f9b 100644 --- a/tests/cases/fourslash/codeFixUseDefaultImport.ts +++ b/tests/cases/fourslash/codeFixUseDefaultImport.ts @@ -21,9 +21,6 @@ ////import * as n from "./non-existant"; ////n; -// @Filename: /f.ts -////export import [|foo|] = require("./a"); - for (const file of ["/b.ts", "/c.ts"]) { goTo.file(file); @@ -41,7 +38,7 @@ a;`, }); } -for (const file of ["/d.ts", "/e.ts", "/f.ts"]) { +for (const file of ["/d.ts", "/e.ts"]) { goTo.file(file); verify.getSuggestionDiagnostics([]); } diff --git a/tests/cases/fourslash/codeFixUseDefaultImport2.ts b/tests/cases/fourslash/codeFixUseDefaultImport2.ts new file mode 100644 index 0000000000000..bbed0853aee71 --- /dev/null +++ b/tests/cases/fourslash/codeFixUseDefaultImport2.ts @@ -0,0 +1,23 @@ +/// + +// @module: commonjs +// @moduleResolution: node +// @allowSyntheticDefaultImports: true + +// @Filename: /node_modules/gensync-BABEL_8_BREAKING-true/index.d.ts +//// export = 'default'; + +// @Filename: /a.ts +//// export import [|foo|] = require("gensync-BABEL_8_BREAKING-true"); + +goTo.file('/a.ts'); +verify.getSuggestionDiagnostics([{ + message: "Import may be converted to a default import.", + code: 80003, +}]); +verify.codeFix({ + description: "Convert to default import", + newFileContent: `import foo from "gensync-BABEL_8_BREAKING-true"; +export { foo }; +`, +}); \ No newline at end of file