From acba122335232b6e2a2b634ab556f3e6c3a03155 Mon Sep 17 00:00:00 2001 From: forfun <834352945@qq.com> Date: Wed, 15 Apr 2020 16:53:41 +0800 Subject: [PATCH 01/19] Feat#Provide a quickfix for non-exported types --- src/compiler/diagnosticMessages.json | 8 +++ .../codefixes/fixImportNonExportedMember.ts | 61 +++++++++++++++++++ src/services/tsconfig.json | 1 + .../codeFixImportNonExportedMember.ts | 27 ++++++++ .../codeFixImportNonExportedMember_all.ts | 28 +++++++++ 5 files changed, 125 insertions(+) create mode 100644 src/services/codefixes/fixImportNonExportedMember.ts create mode 100644 tests/cases/fourslash/codeFixImportNonExportedMember.ts create mode 100644 tests/cases/fourslash/codeFixImportNonExportedMember_all.ts diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 29bf61b2f752b..cd416a0d5eebd 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -5185,6 +5185,14 @@ "category": "Message", "code": 90053 }, + "Export '{0}' from module '{1}'": { + "category": "Message", + "code": 90054 + }, + "Export all non-exported member": { + "category": "Message", + "code": 90055 + }, "Convert function to an ES2015 class": { "category": "Message", "code": 95001 diff --git a/src/services/codefixes/fixImportNonExportedMember.ts b/src/services/codefixes/fixImportNonExportedMember.ts new file mode 100644 index 0000000000000..8085223b381fe --- /dev/null +++ b/src/services/codefixes/fixImportNonExportedMember.ts @@ -0,0 +1,61 @@ +/* @internal */ +namespace ts.codefix { + const fixId = "importNonExportedMember"; + const errorCodes = [ + Diagnostics.Module_0_declares_1_locally_but_it_is_not_exported.code + ]; + registerCodeFix({ + errorCodes, + getCodeActions(context) { + const { sourceFile } = context; + const info = getInfo(sourceFile, context, context.span.start); + if (!info) { + return undefined; + } + const changes = textChanges.ChangeTracker.with(context, t => doChange(t, info.originSourceFile, info.node)); + return [createCodeFixAction(fixId, changes, [Diagnostics.Export_0_from_module_1, info.node.text, showModuleSpecifier(info.importDecl)], fixId, Diagnostics.Export_all_non_exported_member)]; + }, + fixIds: [fixId], + getAllCodeActions: context => codeFixAll(context, errorCodes, (changes, diag) => { + const info = getInfo(diag.file, context, diag.start); + if (info) doChange(changes, info.originSourceFile, info.node); + }), + }); + + interface Info { + readonly node: Identifier; + readonly importDecl: ImportDeclaration; + readonly originSourceFile: SourceFile + } + + function getInfo(sourceFile: SourceFile, context: CodeFixContext | CodeFixAllContext, pos: number): Info | undefined { + const node = getTokenAtPosition(sourceFile, pos); + if (node && isIdentifier(node)) { + const importDecl = tryGetImportFrom(getTokenAtPosition(sourceFile, 0)); + if (!importDecl || !isStringLiteralLike(importDecl.moduleSpecifier)) { + return undefined; + } + const resolvedModule = getResolvedModule(sourceFile, importDecl.moduleSpecifier.text); + const originSourceFile = resolvedModule && context.program.getSourceFile(resolvedModule.resolvedFileName); + if (!originSourceFile) { + return undefined; + } + return { node, importDecl, originSourceFile }; + } + } + + function tryGetImportFrom(token: Node): ImportDeclaration | undefined { + return token.kind === SyntaxKind.ImportKeyword ? tryCast(token.parent, isImportDeclaration) : undefined; + } + + function doChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, node: Identifier): void { + const exportSpecifier = createExportSpecifier(/*propertyName*/ undefined, node); + const exportDeclaration = createExportDeclaration( + /*decorators*/ undefined, + /*modifiers*/ undefined, + createNamedExports([exportSpecifier]), + /*moduleSpecifier*/ undefined, + /*isTypeOnly*/ false); + changes.insertNodeAtEndOfScope(sourceFile, sourceFile, exportDeclaration); + } +} diff --git a/src/services/tsconfig.json b/src/services/tsconfig.json index 117461bc1900c..a09aaabde689e 100644 --- a/src/services/tsconfig.json +++ b/src/services/tsconfig.json @@ -77,6 +77,7 @@ "codefixes/fixModuleAndTargetOptions.ts", "codefixes/fixExtendsInterfaceBecomesImplements.ts", "codefixes/fixForgottenThisPropertyAccess.ts", + "codefixes/fixImportNonExportedMember.ts", "codefixes/fixInvalidJsxCharacters.ts", "codefixes/fixUnusedIdentifier.ts", "codefixes/fixUnreachableCode.ts", diff --git a/tests/cases/fourslash/codeFixImportNonExportedMember.ts b/tests/cases/fourslash/codeFixImportNonExportedMember.ts new file mode 100644 index 0000000000000..5c6e4095e4d1b --- /dev/null +++ b/tests/cases/fourslash/codeFixImportNonExportedMember.ts @@ -0,0 +1,27 @@ +/// + +// @Filename: /a.ts +////declare function foo(): any; +////declare function bar(): any; +////export { foo }; + +// @Filename: /b.ts +////import { bar } from "./a"; + +goTo.file("/b.ts"); +verify.codeFixAvailable([ + { description: `Export 'bar' from module './a'` }, + { description: `Remove import from './a'` }, +]); +verify.codeFix({ + index: 0, + description: `Export 'bar' from module './a'`, + newFileContent: { + '/a.ts': `declare function foo(): any; +declare function bar(): any; +export { foo }; + +export { bar }; +` + } +}); diff --git a/tests/cases/fourslash/codeFixImportNonExportedMember_all.ts b/tests/cases/fourslash/codeFixImportNonExportedMember_all.ts new file mode 100644 index 0000000000000..3dcc13618f6c8 --- /dev/null +++ b/tests/cases/fourslash/codeFixImportNonExportedMember_all.ts @@ -0,0 +1,28 @@ +/// + +// @Filename: /a.ts +////declare function foo(): any; +////declare function bar(): any; +////declare function zoo(): any; +////export { zoo } + +// @Filename: /b.ts +////import { foo, bar } from "./a"; + +goTo.file("/b.ts"); +verify.codeFixAll({ + fixId: "importNonExportedMember", + fixAllDescription: "Export all non-exported member", + newFileContent: { + '/a.ts': `declare function foo(): any; +declare function bar(): any; +declare function zoo(): any; +export { zoo } + +export { foo }; + + +export { bar }; +` + } +}); From 3f17337113b11c68e2312673a8bb277ee11a555a Mon Sep 17 00:00:00 2001 From: forfun <834352945@qq.com> Date: Thu, 16 Apr 2020 09:52:20 +0800 Subject: [PATCH 02/19] fix import start pos --- .../codefixes/fixImportNonExportedMember.ts | 3 ++- .../codeFixImportNonExportedMember.ts | 19 ++++++++++++------- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/services/codefixes/fixImportNonExportedMember.ts b/src/services/codefixes/fixImportNonExportedMember.ts index 8085223b381fe..803dc9daa7e33 100644 --- a/src/services/codefixes/fixImportNonExportedMember.ts +++ b/src/services/codefixes/fixImportNonExportedMember.ts @@ -31,7 +31,8 @@ namespace ts.codefix { function getInfo(sourceFile: SourceFile, context: CodeFixContext | CodeFixAllContext, pos: number): Info | undefined { const node = getTokenAtPosition(sourceFile, pos); if (node && isIdentifier(node)) { - const importDecl = tryGetImportFrom(getTokenAtPosition(sourceFile, 0)); + const importStart = getLineStartPositionForPosition(pos, sourceFile); + const importDecl = tryGetImportFrom(getTokenAtPosition(sourceFile, importStart)); if (!importDecl || !isStringLiteralLike(importDecl.moduleSpecifier)) { return undefined; } diff --git a/tests/cases/fourslash/codeFixImportNonExportedMember.ts b/tests/cases/fourslash/codeFixImportNonExportedMember.ts index 5c6e4095e4d1b..494029312e795 100644 --- a/tests/cases/fourslash/codeFixImportNonExportedMember.ts +++ b/tests/cases/fourslash/codeFixImportNonExportedMember.ts @@ -1,23 +1,28 @@ /// - // @Filename: /a.ts +////declare function zoo(): any; +////export { zoo }; + +// @Filename: /b.ts ////declare function foo(): any; ////declare function bar(): any; ////export { foo }; -// @Filename: /b.ts -////import { bar } from "./a"; +// @Filename: /c.ts +////import { zoo } from "./a"; +////import { bar } from "./b"; -goTo.file("/b.ts"); +goTo.file("/c.ts"); verify.codeFixAvailable([ - { description: `Export 'bar' from module './a'` }, + { description: `Export 'bar' from module './b'` }, { description: `Remove import from './a'` }, + { description: `Remove import from './b'` }, ]); verify.codeFix({ index: 0, - description: `Export 'bar' from module './a'`, + description: `Export 'bar' from module './b'`, newFileContent: { - '/a.ts': `declare function foo(): any; + '/b.ts': `declare function foo(): any; declare function bar(): any; export { foo }; From 2b39b010e5d6dbd3bcdb47f6eb8d7e18038cc56f Mon Sep 17 00:00:00 2001 From: forfun <834352945@qq.com> Date: Tue, 21 Apr 2020 20:18:27 +0800 Subject: [PATCH 03/19] fix according to multiple cases --- .../codefixes/fixImportNonExportedMember.ts | 53 ++++++++++++++++--- src/services/textChanges.ts | 4 ++ ....ts => codeFixImportNonExportedMember1.ts} | 7 +-- .../codeFixImportNonExportedMember2.ts | 40 ++++++++++++++ .../codeFixImportNonExportedMember3.ts | 27 ++++++++++ .../codeFixImportNonExportedMember4.ts | 9 ++++ .../codeFixImportNonExportedMember_all.ts | 12 ++--- 7 files changed, 132 insertions(+), 20 deletions(-) rename tests/cases/fourslash/{codeFixImportNonExportedMember.ts => codeFixImportNonExportedMember1.ts} (87%) create mode 100644 tests/cases/fourslash/codeFixImportNonExportedMember2.ts create mode 100644 tests/cases/fourslash/codeFixImportNonExportedMember3.ts create mode 100644 tests/cases/fourslash/codeFixImportNonExportedMember4.ts diff --git a/src/services/codefixes/fixImportNonExportedMember.ts b/src/services/codefixes/fixImportNonExportedMember.ts index 803dc9daa7e33..5f6ac5de9f398 100644 --- a/src/services/codefixes/fixImportNonExportedMember.ts +++ b/src/services/codefixes/fixImportNonExportedMember.ts @@ -9,7 +9,7 @@ namespace ts.codefix { getCodeActions(context) { const { sourceFile } = context; const info = getInfo(sourceFile, context, context.span.start); - if (!info) { + if (!info || info.originSourceFile.isDeclarationFile) { return undefined; } const changes = textChanges.ChangeTracker.with(context, t => doChange(t, info.originSourceFile, info.node)); @@ -31,8 +31,7 @@ namespace ts.codefix { function getInfo(sourceFile: SourceFile, context: CodeFixContext | CodeFixAllContext, pos: number): Info | undefined { const node = getTokenAtPosition(sourceFile, pos); if (node && isIdentifier(node)) { - const importStart = getLineStartPositionForPosition(pos, sourceFile); - const importDecl = tryGetImportFrom(getTokenAtPosition(sourceFile, importStart)); + const importDecl = findAncestor(node, isImportDeclaration); if (!importDecl || !isStringLiteralLike(importDecl.moduleSpecifier)) { return undefined; } @@ -45,13 +44,55 @@ namespace ts.codefix { } } - function tryGetImportFrom(token: Node): ImportDeclaration | undefined { - return token.kind === SyntaxKind.ImportKeyword ? tryCast(token.parent, isImportDeclaration) : undefined; + function isImportDeclaration(node: Node): node is ImportDeclaration { + return node.kind === SyntaxKind.ImportDeclaration; + } + + function getNamedExportDeclaration(moduleSymbol: Symbol): ExportDeclaration | undefined { + let namedExport; + moduleSymbol.exports?.forEach((symbol) => { + const specifier = symbol.declarations[0]; + if(specifier && isExportSpecifier(specifier) && isNamedExports(specifier.parent)) { + namedExport = specifier.parent?.parent; + } + }); + return namedExport; } function doChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, node: Identifier): void { + const moduleSymbol = sourceFile.localSymbol || sourceFile.symbol; + const localSymbol = moduleSymbol.valueDeclaration.locals?.get(node.escapedText); + if(!localSymbol) { + return; + } + if(isFunctionSymbol(localSymbol)) { + const start = localSymbol.valueDeclaration.pos; + changes.insertExportModifierAtPos(sourceFile, start? start+1:0); + return; + } + + const current: VariableDeclarationList | Node = localSymbol.valueDeclaration.parent; + if(isVariableDeclarationList(current) && current.declarations.length <= 1) { + const start = localSymbol.valueDeclaration.parent.pos; + changes.insertExportModifierAtPos(sourceFile, start? start+1:0); + return; + } + + const namedExportDeclaration = getNamedExportDeclaration(moduleSymbol); + let exportDeclaration; const exportSpecifier = createExportSpecifier(/*propertyName*/ undefined, node); - const exportDeclaration = createExportDeclaration( + if(namedExportDeclaration?.exportClause && isNamedExports(namedExportDeclaration.exportClause)) { + exportDeclaration = updateExportDeclaration( + namedExportDeclaration, + /*decorators*/ undefined, + /*modifiers*/ undefined, + updateNamedExports(namedExportDeclaration.exportClause, namedExportDeclaration.exportClause.elements.concat(exportSpecifier)), + /*moduleSpecifier*/ undefined, + /*isTypeOnly*/ false); + changes.replaceNode(sourceFile, namedExportDeclaration, exportDeclaration); + return; + } + exportDeclaration = createExportDeclaration( /*decorators*/ undefined, /*modifiers*/ undefined, createNamedExports([exportSpecifier]), diff --git a/src/services/textChanges.ts b/src/services/textChanges.ts index bf1dae6560293..927102a966655 100644 --- a/src/services/textChanges.ts +++ b/src/services/textChanges.ts @@ -668,6 +668,10 @@ namespace ts.textChanges { this.insertText(sourceFile, node.getStart(sourceFile), "export "); } + public insertExportModifierAtPos(sourceFile: SourceFile, position: number): void { + this.insertText(sourceFile, position, "export "); + } + /** * This function should be used to insert nodes in lists when nodes don't carry separators as the part of the node range, * i.e. arguments in arguments lists, parameters in parameter lists etc. diff --git a/tests/cases/fourslash/codeFixImportNonExportedMember.ts b/tests/cases/fourslash/codeFixImportNonExportedMember1.ts similarity index 87% rename from tests/cases/fourslash/codeFixImportNonExportedMember.ts rename to tests/cases/fourslash/codeFixImportNonExportedMember1.ts index 494029312e795..f1c5a30ff4299 100644 --- a/tests/cases/fourslash/codeFixImportNonExportedMember.ts +++ b/tests/cases/fourslash/codeFixImportNonExportedMember1.ts @@ -23,10 +23,7 @@ verify.codeFix({ description: `Export 'bar' from module './b'`, newFileContent: { '/b.ts': `declare function foo(): any; -declare function bar(): any; -export { foo }; - -export { bar }; -` +export declare function bar(): any; +export { foo };` } }); diff --git a/tests/cases/fourslash/codeFixImportNonExportedMember2.ts b/tests/cases/fourslash/codeFixImportNonExportedMember2.ts new file mode 100644 index 0000000000000..e1620b1ab4dee --- /dev/null +++ b/tests/cases/fourslash/codeFixImportNonExportedMember2.ts @@ -0,0 +1,40 @@ +/// +// @Filename: /a.ts +////let a = 1, b = 2, c = 3; +////let d = 4; +////export function whatever() { +////} + +// @Filename: /b.ts +////import { a, d } from "./a" + +goTo.file("/b.ts"); +verify.codeFixAvailable([ + { description: `Export 'a' from module './a'` }, + { description: `Export 'd' from module './a'` }, + { description: `Remove import from './a'` }, +]); +verify.codeFix({ + index: 0, + description: `Export 'a' from module './a'`, + newFileContent: { + '/a.ts': `let a = 1, b = 2, c = 3; +let d = 4; +export function whatever() { +} + +export { a }; +` + } +}); + +verify.codeFix({ + index: 1, + description: `Export 'd' from module './a'`, + newFileContent: { + '/a.ts': `let a = 1, b = 2, c = 3; +export let d = 4; +export function whatever() { +}` + } +}); diff --git a/tests/cases/fourslash/codeFixImportNonExportedMember3.ts b/tests/cases/fourslash/codeFixImportNonExportedMember3.ts new file mode 100644 index 0000000000000..18e7c37c6dc67 --- /dev/null +++ b/tests/cases/fourslash/codeFixImportNonExportedMember3.ts @@ -0,0 +1,27 @@ +/// +// @Filename: /a.ts +////let a = 1, b = 2, c = 3; +////let d = 4; +////export function whatever() { +////} +////export { d } + +// @Filename: /b.ts +////import { a, d } from "./a" + +goTo.file("/b.ts"); +verify.codeFixAvailable([ + { description: `Export 'a' from module './a'` }, + { description: `Remove import from './a'` }, +]); +verify.codeFix({ + index: 0, + description: `Export 'a' from module './a'`, + newFileContent: { + '/a.ts': `let a = 1, b = 2, c = 3; +let d = 4; +export function whatever() { +} +export { d, a };` + } +}); diff --git a/tests/cases/fourslash/codeFixImportNonExportedMember4.ts b/tests/cases/fourslash/codeFixImportNonExportedMember4.ts new file mode 100644 index 0000000000000..381a0e0d380b7 --- /dev/null +++ b/tests/cases/fourslash/codeFixImportNonExportedMember4.ts @@ -0,0 +1,9 @@ +/// +// @Filename: /node_modules/foo/index.d.ts +////let a = 0 +////module.exports = 0; + +// @Filename: /a.ts +////import { a } from "foo"; + +verify.not.codeFixAvailable(); \ No newline at end of file diff --git a/tests/cases/fourslash/codeFixImportNonExportedMember_all.ts b/tests/cases/fourslash/codeFixImportNonExportedMember_all.ts index 3dcc13618f6c8..96c61b78fa0e1 100644 --- a/tests/cases/fourslash/codeFixImportNonExportedMember_all.ts +++ b/tests/cases/fourslash/codeFixImportNonExportedMember_all.ts @@ -14,15 +14,9 @@ verify.codeFixAll({ fixId: "importNonExportedMember", fixAllDescription: "Export all non-exported member", newFileContent: { - '/a.ts': `declare function foo(): any; -declare function bar(): any; + '/a.ts': `export declare function foo(): any; +export declare function bar(): any; declare function zoo(): any; -export { zoo } - -export { foo }; - - -export { bar }; -` +export { zoo }` } }); From 3a0b23505e0ae94fdcc9cbe4e3d0ad3a5270bd5b Mon Sep 17 00:00:00 2001 From: forfun <834352945@qq.com> Date: Tue, 28 Apr 2020 15:14:52 +0800 Subject: [PATCH 04/19] code reconstruct --- src/services/textChanges.ts | 2 +- tests/cases/fourslash/codeFixImportNonExportedMember1.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/services/textChanges.ts b/src/services/textChanges.ts index 927102a966655..4f037aafac030 100644 --- a/src/services/textChanges.ts +++ b/src/services/textChanges.ts @@ -665,7 +665,7 @@ namespace ts.textChanges { } public insertExportModifier(sourceFile: SourceFile, node: DeclarationStatement | VariableStatement): void { - this.insertText(sourceFile, node.getStart(sourceFile), "export "); + this.insertExportModifierAtPos(sourceFile, node.getStart(sourceFile)); } public insertExportModifierAtPos(sourceFile: SourceFile, position: number): void { diff --git a/tests/cases/fourslash/codeFixImportNonExportedMember1.ts b/tests/cases/fourslash/codeFixImportNonExportedMember1.ts index f1c5a30ff4299..ef8eb3365ef7d 100644 --- a/tests/cases/fourslash/codeFixImportNonExportedMember1.ts +++ b/tests/cases/fourslash/codeFixImportNonExportedMember1.ts @@ -5,7 +5,7 @@ // @Filename: /b.ts ////declare function foo(): any; -////declare function bar(): any; +////function bar(): any; ////export { foo }; // @Filename: /c.ts @@ -23,7 +23,7 @@ verify.codeFix({ description: `Export 'bar' from module './b'`, newFileContent: { '/b.ts': `declare function foo(): any; -export declare function bar(): any; +export function bar(): any; export { foo };` } }); From 7f7ab7ed19695f6f8cb88ffa2279a79b229e70fa Mon Sep 17 00:00:00 2001 From: forfun <834352945@qq.com> Date: Tue, 28 Apr 2020 15:24:16 +0800 Subject: [PATCH 05/19] modify testcase --- .../codeFixImportNonExportedMember2.ts | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/tests/cases/fourslash/codeFixImportNonExportedMember2.ts b/tests/cases/fourslash/codeFixImportNonExportedMember2.ts index e1620b1ab4dee..d6cda9317e92b 100644 --- a/tests/cases/fourslash/codeFixImportNonExportedMember2.ts +++ b/tests/cases/fourslash/codeFixImportNonExportedMember2.ts @@ -1,25 +1,31 @@ /// + // @Filename: /a.ts ////let a = 1, b = 2, c = 3; -////let d = 4; ////export function whatever() { ////} // @Filename: /b.ts -////import { a, d } from "./a" +////let d = 4; +////export function whatever2() { +////} -goTo.file("/b.ts"); +// @Filename: /c.ts +////import { a } from "./a" +////import { d } from "./b" + +goTo.file("/c.ts"); verify.codeFixAvailable([ { description: `Export 'a' from module './a'` }, - { description: `Export 'd' from module './a'` }, + { description: `Export 'd' from module './b'` }, { description: `Remove import from './a'` }, + { description: `Remove import from './b'` }, ]); verify.codeFix({ index: 0, description: `Export 'a' from module './a'`, newFileContent: { '/a.ts': `let a = 1, b = 2, c = 3; -let d = 4; export function whatever() { } @@ -30,11 +36,10 @@ export { a }; verify.codeFix({ index: 1, - description: `Export 'd' from module './a'`, + description: `Export 'd' from module './b'`, newFileContent: { - '/a.ts': `let a = 1, b = 2, c = 3; -export let d = 4; -export function whatever() { + '/b.ts': `export let d = 4; +export function whatever2() { }` } }); From 4a0b533d887e991676a84d50780dd032f028ffcd Mon Sep 17 00:00:00 2001 From: forfun <834352945@qq.com> Date: Tue, 28 Apr 2020 15:56:38 +0800 Subject: [PATCH 06/19] modify testcase message --- tests/cases/fourslash/codeFixImportNonExportedMember_all.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/cases/fourslash/codeFixImportNonExportedMember_all.ts b/tests/cases/fourslash/codeFixImportNonExportedMember_all.ts index 96c61b78fa0e1..1edc06da5dac1 100644 --- a/tests/cases/fourslash/codeFixImportNonExportedMember_all.ts +++ b/tests/cases/fourslash/codeFixImportNonExportedMember_all.ts @@ -12,7 +12,7 @@ goTo.file("/b.ts"); verify.codeFixAll({ fixId: "importNonExportedMember", - fixAllDescription: "Export all non-exported member", + fixAllDescription: ts.Diagnostics.Export_all_non_exported_member.message, newFileContent: { '/a.ts': `export declare function foo(): any; export declare function bar(): any; From 75cc9099e6d1294563c227b71593fa42ec851072 Mon Sep 17 00:00:00 2001 From: Chunlin Date: Wed, 6 May 2020 10:50:52 +0800 Subject: [PATCH 07/19] Update src/services/codefixes/fixImportNonExportedMember.ts Co-authored-by: Daniel Rosenwasser --- src/services/codefixes/fixImportNonExportedMember.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/codefixes/fixImportNonExportedMember.ts b/src/services/codefixes/fixImportNonExportedMember.ts index 5f6ac5de9f398..b182c1ffcd862 100644 --- a/src/services/codefixes/fixImportNonExportedMember.ts +++ b/src/services/codefixes/fixImportNonExportedMember.ts @@ -74,7 +74,7 @@ namespace ts.codefix { const current: VariableDeclarationList | Node = localSymbol.valueDeclaration.parent; if(isVariableDeclarationList(current) && current.declarations.length <= 1) { const start = localSymbol.valueDeclaration.parent.pos; - changes.insertExportModifierAtPos(sourceFile, start? start+1:0); + changes.insertExportModifierAtPos(sourceFile, start ? start + 1 : 0); return; } From 83d07ebe6071bb8954d933303c37f80e38d1a747 Mon Sep 17 00:00:00 2001 From: forfun <834352945@qq.com> Date: Mon, 11 May 2020 11:38:01 +0800 Subject: [PATCH 08/19] add space --- src/services/codefixes/fixImportNonExportedMember.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/codefixes/fixImportNonExportedMember.ts b/src/services/codefixes/fixImportNonExportedMember.ts index b182c1ffcd862..c91bd3ae11814 100644 --- a/src/services/codefixes/fixImportNonExportedMember.ts +++ b/src/services/codefixes/fixImportNonExportedMember.ts @@ -67,7 +67,7 @@ namespace ts.codefix { } if(isFunctionSymbol(localSymbol)) { const start = localSymbol.valueDeclaration.pos; - changes.insertExportModifierAtPos(sourceFile, start? start+1:0); + changes.insertExportModifierAtPos(sourceFile, start ? start + 1 : 0); return; } From 223d8963fc563a1cfd0b44168932f3f992f84706 Mon Sep 17 00:00:00 2001 From: Chunlin Date: Thu, 21 May 2020 14:43:11 +0800 Subject: [PATCH 09/19] Update fixImportNonExportedMember.ts --- src/services/codefixes/fixImportNonExportedMember.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/services/codefixes/fixImportNonExportedMember.ts b/src/services/codefixes/fixImportNonExportedMember.ts index c91bd3ae11814..ba29141d27ae6 100644 --- a/src/services/codefixes/fixImportNonExportedMember.ts +++ b/src/services/codefixes/fixImportNonExportedMember.ts @@ -92,6 +92,7 @@ namespace ts.codefix { changes.replaceNode(sourceFile, namedExportDeclaration, exportDeclaration); return; } + exportDeclaration = createExportDeclaration( /*decorators*/ undefined, /*modifiers*/ undefined, From e4f120b92b6d5b906ce8fe442d5b4154c85bfb53 Mon Sep 17 00:00:00 2001 From: Chunlin Date: Fri, 22 May 2020 11:22:58 +0800 Subject: [PATCH 10/19] Update fixImportNonExportedMember.ts --- src/services/codefixes/fixImportNonExportedMember.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/services/codefixes/fixImportNonExportedMember.ts b/src/services/codefixes/fixImportNonExportedMember.ts index ba29141d27ae6..c91bd3ae11814 100644 --- a/src/services/codefixes/fixImportNonExportedMember.ts +++ b/src/services/codefixes/fixImportNonExportedMember.ts @@ -92,7 +92,6 @@ namespace ts.codefix { changes.replaceNode(sourceFile, namedExportDeclaration, exportDeclaration); return; } - exportDeclaration = createExportDeclaration( /*decorators*/ undefined, /*modifiers*/ undefined, From b6836173f316799ab810bdb703a0c8d16af0c200 Mon Sep 17 00:00:00 2001 From: Chunlin Date: Thu, 18 Jun 2020 14:36:13 +0800 Subject: [PATCH 11/19] Update src/services/codefixes/fixImportNonExportedMember.ts Co-authored-by: Eli Barzilay --- .../codefixes/fixImportNonExportedMember.ts | 24 +++++++++---------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/src/services/codefixes/fixImportNonExportedMember.ts b/src/services/codefixes/fixImportNonExportedMember.ts index c91bd3ae11814..aeefff09e0baf 100644 --- a/src/services/codefixes/fixImportNonExportedMember.ts +++ b/src/services/codefixes/fixImportNonExportedMember.ts @@ -79,25 +79,23 @@ namespace ts.codefix { } const namedExportDeclaration = getNamedExportDeclaration(moduleSymbol); - let exportDeclaration; const exportSpecifier = createExportSpecifier(/*propertyName*/ undefined, node); - if(namedExportDeclaration?.exportClause && isNamedExports(namedExportDeclaration.exportClause)) { - exportDeclaration = updateExportDeclaration( + if (namedExportDeclaration?.exportClause && isNamedExports(namedExportDeclaration.exportClause)) { + return changes.replaceNode(sourceFile, namedExportDeclaration, updateExportDeclaration( namedExportDeclaration, /*decorators*/ undefined, /*modifiers*/ undefined, updateNamedExports(namedExportDeclaration.exportClause, namedExportDeclaration.exportClause.elements.concat(exportSpecifier)), /*moduleSpecifier*/ undefined, - /*isTypeOnly*/ false); - changes.replaceNode(sourceFile, namedExportDeclaration, exportDeclaration); - return; + /*isTypeOnly*/ false)); + } + else { + return changes.insertNodeAtEndOfScope(sourceFile, sourceFile, createExportDeclaration( + /*decorators*/ undefined, + /*modifiers*/ undefined, + createNamedExports([exportSpecifier]), + /*moduleSpecifier*/ undefined, + /*isTypeOnly*/ false)); } - exportDeclaration = createExportDeclaration( - /*decorators*/ undefined, - /*modifiers*/ undefined, - createNamedExports([exportSpecifier]), - /*moduleSpecifier*/ undefined, - /*isTypeOnly*/ false); - changes.insertNodeAtEndOfScope(sourceFile, sourceFile, exportDeclaration); } } From afb48c984d0de7b6c405fbd0f576491364bcdef5 Mon Sep 17 00:00:00 2001 From: Qiyu8 Date: Thu, 18 Jun 2020 14:48:08 +0800 Subject: [PATCH 12/19] modified accordind to review --- src/compiler/diagnosticMessages.json | 2 +- .../codefixes/fixImportNonExportedMember.ts | 17 +++++++---------- src/services/textChanges.ts | 4 ++-- 3 files changed, 10 insertions(+), 13 deletions(-) diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index cd416a0d5eebd..034cf4674e8ea 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -5189,7 +5189,7 @@ "category": "Message", "code": 90054 }, - "Export all non-exported member": { + "Add all missing exports": { "category": "Message", "code": 90055 }, diff --git a/src/services/codefixes/fixImportNonExportedMember.ts b/src/services/codefixes/fixImportNonExportedMember.ts index c91bd3ae11814..14e08ffa51150 100644 --- a/src/services/codefixes/fixImportNonExportedMember.ts +++ b/src/services/codefixes/fixImportNonExportedMember.ts @@ -31,7 +31,7 @@ namespace ts.codefix { function getInfo(sourceFile: SourceFile, context: CodeFixContext | CodeFixAllContext, pos: number): Info | undefined { const node = getTokenAtPosition(sourceFile, pos); if (node && isIdentifier(node)) { - const importDecl = findAncestor(node, isImportDeclaration); + const importDecl = findAncestor(node, node => node.kind === SyntaxKind.ImportDeclaration); if (!importDecl || !isStringLiteralLike(importDecl.moduleSpecifier)) { return undefined; } @@ -44,15 +44,12 @@ namespace ts.codefix { } } - function isImportDeclaration(node: Node): node is ImportDeclaration { - return node.kind === SyntaxKind.ImportDeclaration; - } - function getNamedExportDeclaration(moduleSymbol: Symbol): ExportDeclaration | undefined { let namedExport; moduleSymbol.exports?.forEach((symbol) => { const specifier = symbol.declarations[0]; - if(specifier && isExportSpecifier(specifier) && isNamedExports(specifier.parent)) { + if (specifier && isExportSpecifier(specifier) + && specifier.parent && isNamedExports(specifier.parent)) { namedExport = specifier.parent?.parent; } }); @@ -62,19 +59,19 @@ namespace ts.codefix { function doChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, node: Identifier): void { const moduleSymbol = sourceFile.localSymbol || sourceFile.symbol; const localSymbol = moduleSymbol.valueDeclaration.locals?.get(node.escapedText); - if(!localSymbol) { + if (!localSymbol) { return; } - if(isFunctionSymbol(localSymbol)) { + if (isFunctionSymbol(localSymbol)) { const start = localSymbol.valueDeclaration.pos; - changes.insertExportModifierAtPos(sourceFile, start ? start + 1 : 0); + changes.insertExportModifierAt(sourceFile, start ? start + 1 : 0); return; } const current: VariableDeclarationList | Node = localSymbol.valueDeclaration.parent; if(isVariableDeclarationList(current) && current.declarations.length <= 1) { const start = localSymbol.valueDeclaration.parent.pos; - changes.insertExportModifierAtPos(sourceFile, start ? start + 1 : 0); + changes.insertExportModifierAt(sourceFile, start ? start + 1 : 0); return; } diff --git a/src/services/textChanges.ts b/src/services/textChanges.ts index 4f037aafac030..c098e28977c28 100644 --- a/src/services/textChanges.ts +++ b/src/services/textChanges.ts @@ -665,10 +665,10 @@ namespace ts.textChanges { } public insertExportModifier(sourceFile: SourceFile, node: DeclarationStatement | VariableStatement): void { - this.insertExportModifierAtPos(sourceFile, node.getStart(sourceFile)); + this.insertExportModifierAt(sourceFile, node.getStart(sourceFile)); } - public insertExportModifierAtPos(sourceFile: SourceFile, position: number): void { + public insertExportModifierAt(sourceFile: SourceFile, position: number): void { this.insertText(sourceFile, position, "export "); } From 2b4cbd5b6661279db85252a330d89afad0c91b0e Mon Sep 17 00:00:00 2001 From: Qiyu8 Date: Thu, 18 Jun 2020 15:03:37 +0800 Subject: [PATCH 13/19] modified accordind to review --- src/services/codefixes/fixImportNonExportedMember.ts | 2 +- tests/cases/fourslash/codeFixImportNonExportedMember_all.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/services/codefixes/fixImportNonExportedMember.ts b/src/services/codefixes/fixImportNonExportedMember.ts index 6390f6519ce5b..db1c5a57be3ae 100644 --- a/src/services/codefixes/fixImportNonExportedMember.ts +++ b/src/services/codefixes/fixImportNonExportedMember.ts @@ -13,7 +13,7 @@ namespace ts.codefix { return undefined; } const changes = textChanges.ChangeTracker.with(context, t => doChange(t, info.originSourceFile, info.node)); - return [createCodeFixAction(fixId, changes, [Diagnostics.Export_0_from_module_1, info.node.text, showModuleSpecifier(info.importDecl)], fixId, Diagnostics.Export_all_non_exported_member)]; + return [createCodeFixAction(fixId, changes, [Diagnostics.Export_0_from_module_1, info.node.text, showModuleSpecifier(info.importDecl)], fixId, Diagnostics.Add_all_missing_exports)]; }, fixIds: [fixId], getAllCodeActions: context => codeFixAll(context, errorCodes, (changes, diag) => { diff --git a/tests/cases/fourslash/codeFixImportNonExportedMember_all.ts b/tests/cases/fourslash/codeFixImportNonExportedMember_all.ts index 1edc06da5dac1..8486a0680ae36 100644 --- a/tests/cases/fourslash/codeFixImportNonExportedMember_all.ts +++ b/tests/cases/fourslash/codeFixImportNonExportedMember_all.ts @@ -12,7 +12,7 @@ goTo.file("/b.ts"); verify.codeFixAll({ fixId: "importNonExportedMember", - fixAllDescription: ts.Diagnostics.Export_all_non_exported_member.message, + fixAllDescription: ts.Diagnostics.Add_all_missing_exports.message, newFileContent: { '/a.ts': `export declare function foo(): any; export declare function bar(): any; From e03ee01031a166ddc5878bb3c488861fd14f7e5e Mon Sep 17 00:00:00 2001 From: Qiyu8 Date: Thu, 18 Jun 2020 16:52:35 +0800 Subject: [PATCH 14/19] modified accordind to review --- src/services/codefixes/fixImportNonExportedMember.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/services/codefixes/fixImportNonExportedMember.ts b/src/services/codefixes/fixImportNonExportedMember.ts index db1c5a57be3ae..f4252b934c938 100644 --- a/src/services/codefixes/fixImportNonExportedMember.ts +++ b/src/services/codefixes/fixImportNonExportedMember.ts @@ -27,11 +27,14 @@ namespace ts.codefix { readonly importDecl: ImportDeclaration; readonly originSourceFile: SourceFile } + function isImportDeclaration(node: Node): node is ImportDeclaration { + return node.kind === SyntaxKind.ImportDeclaration; + } function getInfo(sourceFile: SourceFile, context: CodeFixContext | CodeFixAllContext, pos: number): Info | undefined { const node = getTokenAtPosition(sourceFile, pos); if (node && isIdentifier(node)) { - const importDecl = findAncestor(node, node => node.kind === SyntaxKind.ImportDeclaration); + const importDecl = findAncestor(node, isImportDeclaration); if (!importDecl || !isStringLiteralLike(importDecl.moduleSpecifier)) { return undefined; } @@ -69,7 +72,7 @@ namespace ts.codefix { } const current: VariableDeclarationList | Node = localSymbol.valueDeclaration.parent; - if(isVariableDeclarationList(current) && current.declarations.length <= 1) { + if (isVariableDeclarationList(current) && current.declarations.length <= 1) { const start = localSymbol.valueDeclaration.parent.pos; changes.insertExportModifierAt(sourceFile, start ? start + 1 : 0); return; From 3ee7a66e9c5038fdaae5bc203da5b69f72adec35 Mon Sep 17 00:00:00 2001 From: Chunlin Date: Wed, 1 Jul 2020 11:37:36 +0800 Subject: [PATCH 15/19] Update src/services/codefixes/fixImportNonExportedMember.ts Co-authored-by: Daniel Rosenwasser --- src/services/codefixes/fixImportNonExportedMember.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/codefixes/fixImportNonExportedMember.ts b/src/services/codefixes/fixImportNonExportedMember.ts index f4252b934c938..8df809de3827e 100644 --- a/src/services/codefixes/fixImportNonExportedMember.ts +++ b/src/services/codefixes/fixImportNonExportedMember.ts @@ -49,7 +49,7 @@ namespace ts.codefix { function getNamedExportDeclaration(moduleSymbol: Symbol): ExportDeclaration | undefined { let namedExport; - moduleSymbol.exports?.forEach((symbol) => { + forEach(moduleSymbol.exports, symbol => { const specifier = symbol.declarations[0]; if (specifier && isExportSpecifier(specifier) && specifier.parent && isNamedExports(specifier.parent)) { From 9f7dcd91e071394fb4b6aeea26ec3fe240ba1d5a Mon Sep 17 00:00:00 2001 From: Chunlin Date: Wed, 1 Jul 2020 11:37:45 +0800 Subject: [PATCH 16/19] Update src/services/codefixes/fixImportNonExportedMember.ts Co-authored-by: Daniel Rosenwasser --- src/services/codefixes/fixImportNonExportedMember.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/services/codefixes/fixImportNonExportedMember.ts b/src/services/codefixes/fixImportNonExportedMember.ts index 8df809de3827e..5b265377f1d73 100644 --- a/src/services/codefixes/fixImportNonExportedMember.ts +++ b/src/services/codefixes/fixImportNonExportedMember.ts @@ -51,8 +51,8 @@ namespace ts.codefix { let namedExport; forEach(moduleSymbol.exports, symbol => { const specifier = symbol.declarations[0]; - if (specifier && isExportSpecifier(specifier) - && specifier.parent && isNamedExports(specifier.parent)) { + if (specifier && isExportSpecifier(specifier) && specifier.parent && isNamedExports(specifier.parent)) { + namedExport = specifier.parent?.parent; } }); From 2289ea09a85a003f5fe906c2387699bcf611bd65 Mon Sep 17 00:00:00 2001 From: Qiyu8 Date: Tue, 4 Aug 2020 14:32:13 +0800 Subject: [PATCH 17/19] add order and fix CI errors --- .../codefixes/fixImportNonExportedMember.ts | 47 ++++++++++++------- .../codeFixImportNonExportedMember3.ts | 2 +- 2 files changed, 31 insertions(+), 18 deletions(-) diff --git a/src/services/codefixes/fixImportNonExportedMember.ts b/src/services/codefixes/fixImportNonExportedMember.ts index 5b265377f1d73..2c116b486fdc2 100644 --- a/src/services/codefixes/fixImportNonExportedMember.ts +++ b/src/services/codefixes/fixImportNonExportedMember.ts @@ -47,18 +47,28 @@ namespace ts.codefix { } } - function getNamedExportDeclaration(moduleSymbol: Symbol): ExportDeclaration | undefined { + function getNamedExportDeclaration(sourceFile: SourceFile): ExportDeclaration | undefined { let namedExport; - forEach(moduleSymbol.exports, symbol => { - const specifier = symbol.declarations[0]; - if (specifier && isExportSpecifier(specifier) && specifier.parent && isNamedExports(specifier.parent)) { - - namedExport = specifier.parent?.parent; + const statements = sourceFile.statements.filter(isExportDeclaration); + for (const statement of statements) { + if (statement.exportClause && isNamedExports(statement.exportClause)) { + namedExport = statement; } - }); + } return namedExport; } + function compareIdentifiers(s1: Identifier, s2: Identifier) { + return compareStringsCaseInsensitive(s1.text, s2.text); + } + + function sortSpecifiers(specifiers: ExportSpecifier[]): readonly ExportSpecifier[] { + return stableSort(specifiers, function (s1, s2) { + return compareIdentifiers(s1.propertyName || s1.name, s2.propertyName || s2.name) || + compareIdentifiers(s1.name, s2.name); + }); + } + function doChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, node: Identifier): void { const moduleSymbol = sourceFile.localSymbol || sourceFile.symbol; const localSymbol = moduleSymbol.valueDeclaration.locals?.get(node.escapedText); @@ -78,24 +88,27 @@ namespace ts.codefix { return; } - const namedExportDeclaration = getNamedExportDeclaration(moduleSymbol); - const exportSpecifier = createExportSpecifier(/*propertyName*/ undefined, node); + const namedExportDeclaration = getNamedExportDeclaration(sourceFile); + const exportSpecifier = factory.createExportSpecifier(/*propertyName*/ undefined, node); if (namedExportDeclaration?.exportClause && isNamedExports(namedExportDeclaration.exportClause)) { - return changes.replaceNode(sourceFile, namedExportDeclaration, updateExportDeclaration( + const sortedExportSpecifiers = sortSpecifiers(namedExportDeclaration.exportClause.elements.concat(exportSpecifier)); + return changes.replaceNode(sourceFile, namedExportDeclaration, factory.updateExportDeclaration( namedExportDeclaration, /*decorators*/ undefined, /*modifiers*/ undefined, - updateNamedExports(namedExportDeclaration.exportClause, namedExportDeclaration.exportClause.elements.concat(exportSpecifier)), - /*moduleSpecifier*/ undefined, - /*isTypeOnly*/ false)); + /*isTypeOnly*/ false, + factory.updateNamedExports(namedExportDeclaration.exportClause, sortedExportSpecifiers), + /*moduleSpecifier*/ undefined + )); } else { - return changes.insertNodeAtEndOfScope(sourceFile, sourceFile, createExportDeclaration( + return changes.insertNodeAtEndOfScope(sourceFile, sourceFile, factory.createExportDeclaration( /*decorators*/ undefined, /*modifiers*/ undefined, - createNamedExports([exportSpecifier]), - /*moduleSpecifier*/ undefined, - /*isTypeOnly*/ false)); + /*isTypeOnly*/ false, + factory.createNamedExports([exportSpecifier]), + /*moduleSpecifier*/ undefined + )); } } } diff --git a/tests/cases/fourslash/codeFixImportNonExportedMember3.ts b/tests/cases/fourslash/codeFixImportNonExportedMember3.ts index 18e7c37c6dc67..2927ba48cf6d4 100644 --- a/tests/cases/fourslash/codeFixImportNonExportedMember3.ts +++ b/tests/cases/fourslash/codeFixImportNonExportedMember3.ts @@ -22,6 +22,6 @@ verify.codeFix({ let d = 4; export function whatever() { } -export { d, a };` +export { a, d };` } }); From 5a68eb28d2fabf13efb625f78759b3158c29d656 Mon Sep 17 00:00:00 2001 From: Qiyu8 Date: Tue, 4 Aug 2020 15:00:28 +0800 Subject: [PATCH 18/19] fix lint error --- src/services/codefixes/fixImportNonExportedMember.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/codefixes/fixImportNonExportedMember.ts b/src/services/codefixes/fixImportNonExportedMember.ts index 2c116b486fdc2..3bb7b8f51a12a 100644 --- a/src/services/codefixes/fixImportNonExportedMember.ts +++ b/src/services/codefixes/fixImportNonExportedMember.ts @@ -63,7 +63,7 @@ namespace ts.codefix { } function sortSpecifiers(specifiers: ExportSpecifier[]): readonly ExportSpecifier[] { - return stableSort(specifiers, function (s1, s2) { + return stableSort(specifiers, (s1, s2) => { return compareIdentifiers(s1.propertyName || s1.name, s2.propertyName || s2.name) || compareIdentifiers(s1.name, s2.name); }); From 85d5b4aee3f05c12a2030f82307a0886714d70e3 Mon Sep 17 00:00:00 2001 From: Qiyu8 Date: Thu, 6 Aug 2020 08:52:22 +0800 Subject: [PATCH 19/19] review update --- src/services/codefixes/fixImportNonExportedMember.ts | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/services/codefixes/fixImportNonExportedMember.ts b/src/services/codefixes/fixImportNonExportedMember.ts index 3bb7b8f51a12a..10c3590273334 100644 --- a/src/services/codefixes/fixImportNonExportedMember.ts +++ b/src/services/codefixes/fixImportNonExportedMember.ts @@ -27,9 +27,6 @@ namespace ts.codefix { readonly importDecl: ImportDeclaration; readonly originSourceFile: SourceFile } - function isImportDeclaration(node: Node): node is ImportDeclaration { - return node.kind === SyntaxKind.ImportDeclaration; - } function getInfo(sourceFile: SourceFile, context: CodeFixContext | CodeFixAllContext, pos: number): Info | undefined { const node = getTokenAtPosition(sourceFile, pos); @@ -49,9 +46,9 @@ namespace ts.codefix { function getNamedExportDeclaration(sourceFile: SourceFile): ExportDeclaration | undefined { let namedExport; - const statements = sourceFile.statements.filter(isExportDeclaration); - for (const statement of statements) { - if (statement.exportClause && isNamedExports(statement.exportClause)) { + for (const statement of sourceFile.statements) { + if (isExportDeclaration(statement) && statement.exportClause && + isNamedExports(statement.exportClause)) { namedExport = statement; } } @@ -64,8 +61,7 @@ namespace ts.codefix { function sortSpecifiers(specifiers: ExportSpecifier[]): readonly ExportSpecifier[] { return stableSort(specifiers, (s1, s2) => { - return compareIdentifiers(s1.propertyName || s1.name, s2.propertyName || s2.name) || - compareIdentifiers(s1.name, s2.name); + return compareIdentifiers(s1.propertyName || s1.name, s2.propertyName || s2.name); }); }