From f4141ac6bfd726becafb9eef00adb79afbc1f027 Mon Sep 17 00:00:00 2001 From: Andrew Casey Date: Tue, 13 Feb 2018 14:52:15 -0800 Subject: [PATCH] Separate OrganizeImports into its own namespace and file --- src/harness/unittests/organizeImports.ts | 42 ++-- src/services/organizeImports.ts | 291 +++++++++++++++++++++++ src/services/services.ts | 280 +--------------------- 3 files changed, 314 insertions(+), 299 deletions(-) create mode 100644 src/services/organizeImports.ts diff --git a/src/harness/unittests/organizeImports.ts b/src/harness/unittests/organizeImports.ts index e78a88ca232..eaa21bbb72f 100644 --- a/src/harness/unittests/organizeImports.ts +++ b/src/harness/unittests/organizeImports.ts @@ -6,12 +6,12 @@ namespace ts { describe("Organize imports", () => { describe("Sort imports", () => { it("No imports", () => { - assert.isEmpty(sortImports([])); + assert.isEmpty(OrganizeImports.sortImports([])); }); it("One import", () => { const unsortedImports = parseImports(`import "lib";`); - const actualSortedImports = sortImports(unsortedImports); + const actualSortedImports = OrganizeImports.sortImports(unsortedImports); const expectedSortedImports = unsortedImports; assertListEqual(expectedSortedImports, actualSortedImports); }); @@ -84,27 +84,27 @@ namespace ts { function assertUnaffectedBySort(...importStrings: string[]) { const unsortedImports1 = parseImports(...importStrings); - assertListEqual(unsortedImports1, sortImports(unsortedImports1)); + assertListEqual(unsortedImports1, OrganizeImports.sortImports(unsortedImports1)); const unsortedImports2 = reverse(unsortedImports1); - assertListEqual(unsortedImports2, sortImports(unsortedImports2)); + assertListEqual(unsortedImports2, OrganizeImports.sortImports(unsortedImports2)); } function assertSortsBefore(importString1: string, importString2: string) { const imports = parseImports(importString1, importString2); - assertListEqual(imports, sortImports(imports)); - assertListEqual(imports, sortImports(reverse(imports))); + assertListEqual(imports, OrganizeImports.sortImports(imports)); + assertListEqual(imports, OrganizeImports.sortImports(reverse(imports))); } }); describe("Coalesce imports", () => { it("No imports", () => { - assert.isEmpty(coalesceImports([])); + assert.isEmpty(OrganizeImports.coalesceImports([])); }); it("Sort specifiers", () => { const sortedImports = parseImports(`import { default as m, a as n, b, y, z as o } from "lib";`); - const actualCoalescedImports = coalesceImports(sortedImports); + const actualCoalescedImports = OrganizeImports.coalesceImports(sortedImports); const expectedCoalescedImports = parseImports(`import { a as n, b, default as m, y, z as o } from "lib";`); assertListEqual(expectedCoalescedImports, actualCoalescedImports); }); @@ -113,7 +113,7 @@ namespace ts { const sortedImports = parseImports( `import "lib";`, `import "lib";`); - const actualCoalescedImports = coalesceImports(sortedImports); + const actualCoalescedImports = OrganizeImports.coalesceImports(sortedImports); const expectedCoalescedImports = parseImports(`import "lib";`); assertListEqual(expectedCoalescedImports, actualCoalescedImports); }); @@ -122,7 +122,7 @@ namespace ts { const sortedImports = parseImports( `import * as x from "lib";`, `import * as y from "lib";`); - const actualCoalescedImports = coalesceImports(sortedImports); + const actualCoalescedImports = OrganizeImports.coalesceImports(sortedImports); const expectedCoalescedImports = sortedImports; assertListEqual(expectedCoalescedImports, actualCoalescedImports); }); @@ -131,7 +131,7 @@ namespace ts { const sortedImports = parseImports( `import x from "lib";`, `import y from "lib";`); - const actualCoalescedImports = coalesceImports(sortedImports); + const actualCoalescedImports = OrganizeImports.coalesceImports(sortedImports); const expectedCoalescedImports = parseImports(`import { default as x, default as y } from "lib";`); assertListEqual(expectedCoalescedImports, actualCoalescedImports); }); @@ -140,7 +140,7 @@ namespace ts { const sortedImports = parseImports( `import { x } from "lib";`, `import { y as z } from "lib";`); - const actualCoalescedImports = coalesceImports(sortedImports); + const actualCoalescedImports = OrganizeImports.coalesceImports(sortedImports); const expectedCoalescedImports = parseImports(`import { x, y as z } from "lib";`); assertListEqual(expectedCoalescedImports, actualCoalescedImports); }); @@ -149,7 +149,7 @@ namespace ts { const sortedImports = parseImports( `import "lib";`, `import * as x from "lib";`); - const actualCoalescedImports = coalesceImports(sortedImports); + const actualCoalescedImports = OrganizeImports.coalesceImports(sortedImports); const expectedCoalescedImports = sortedImports; assertListEqual(expectedCoalescedImports, actualCoalescedImports); }); @@ -158,7 +158,7 @@ namespace ts { const sortedImports = parseImports( `import "lib";`, `import x from "lib";`); - const actualCoalescedImports = coalesceImports(sortedImports); + const actualCoalescedImports = OrganizeImports.coalesceImports(sortedImports); const expectedCoalescedImports = sortedImports; assertListEqual(expectedCoalescedImports, actualCoalescedImports); }); @@ -167,7 +167,7 @@ namespace ts { const sortedImports = parseImports( `import "lib";`, `import { x } from "lib";`); - const actualCoalescedImports = coalesceImports(sortedImports); + const actualCoalescedImports = OrganizeImports.coalesceImports(sortedImports); const expectedCoalescedImports = sortedImports; assertListEqual(expectedCoalescedImports, actualCoalescedImports); }); @@ -176,7 +176,7 @@ namespace ts { const sortedImports = parseImports( `import * as x from "lib";`, `import y from "lib";`); - const actualCoalescedImports = coalesceImports(sortedImports); + const actualCoalescedImports = OrganizeImports.coalesceImports(sortedImports); const expectedCoalescedImports = parseImports( `import y, * as x from "lib";`); assertListEqual(expectedCoalescedImports, actualCoalescedImports); @@ -186,7 +186,7 @@ namespace ts { const sortedImports = parseImports( `import * as x from "lib";`, `import { y } from "lib";`); - const actualCoalescedImports = coalesceImports(sortedImports); + const actualCoalescedImports = OrganizeImports.coalesceImports(sortedImports); const expectedCoalescedImports = sortedImports; assertListEqual(expectedCoalescedImports, actualCoalescedImports); }); @@ -195,7 +195,7 @@ namespace ts { const sortedImports = parseImports( `import x from "lib";`, `import { y } from "lib";`); - const actualCoalescedImports = coalesceImports(sortedImports); + const actualCoalescedImports = OrganizeImports.coalesceImports(sortedImports); const expectedCoalescedImports = parseImports( `import x, { y } from "lib";`); assertListEqual(expectedCoalescedImports, actualCoalescedImports); @@ -211,7 +211,7 @@ namespace ts { `import * as x from "lib";`, `import z from "lib";`, `import { a } from "lib";`); - const actualCoalescedImports = coalesceImports(sortedImports); + const actualCoalescedImports = OrganizeImports.coalesceImports(sortedImports); const expectedCoalescedImports = parseImports( `import "lib";`, `import * as x from "lib";`, @@ -226,7 +226,7 @@ namespace ts { `import { b } from "lib1";`, `import { c } from "lib2";`, `import { a } from "lib2";`); - const actualCoalescedImports = coalesceImports(sortedImports); + const actualCoalescedImports = OrganizeImports.coalesceImports(sortedImports); const expectedCoalescedImports = parseImports( `import { b, d } from "lib1";`, `import { a, c } from "lib2";`); @@ -239,7 +239,7 @@ namespace ts { `import * as x from "lib";`, `import * as y from "lib";`, `import z from "lib";`); - const actualCoalescedImports = coalesceImports(sortedImports); + const actualCoalescedImports = OrganizeImports.coalesceImports(sortedImports); const expectedCoalescedImports = sortedImports; assertListEqual(expectedCoalescedImports, actualCoalescedImports); }); diff --git a/src/services/organizeImports.ts b/src/services/organizeImports.ts new file mode 100644 index 00000000000..aaff3331941 --- /dev/null +++ b/src/services/organizeImports.ts @@ -0,0 +1,291 @@ +/* @internal */ +namespace ts.OrganizeImports { + export function organizeImports( + sourceFile: SourceFile, + formatContext: formatting.FormatContext, + host: LanguageServiceHost, + cancellationToken: CancellationToken) { + + // All of the (old) ImportDeclarations in the file, in syntactic order. + const oldImportDecls: ImportDeclaration[] = []; + + forEachChild(sourceFile, node => { + cancellationToken.throwIfCancellationRequested(); + if (isImportDeclaration(node)) { + oldImportDecls.push(node); + } + // TODO (https://github.com/Microsoft/TypeScript/issues/10020): sort *within* ambient modules (find using isAmbientModule) + }); + + if (oldImportDecls.length === 0) { + return []; + } + + const usedImportDecls = removeUnusedImports(oldImportDecls); + cancellationToken.throwIfCancellationRequested(); + const sortedImportDecls = sortImports(usedImportDecls); + cancellationToken.throwIfCancellationRequested(); + const coalescedImportDecls = coalesceImports(sortedImportDecls); + cancellationToken.throwIfCancellationRequested(); + + // All of the (new) ImportDeclarations in the file, in sorted order. + const newImportDecls = coalescedImportDecls; + + const changeTracker = textChanges.ChangeTracker.fromContext({ host, formatContext }); + + // NB: Stopping before i === 0 + for (let i = oldImportDecls.length - 1; i > 0; i--) { + changeTracker.deleteNode(sourceFile, oldImportDecls[i]); + } + + if (newImportDecls.length === 0) { + changeTracker.deleteNode(sourceFile, oldImportDecls[0]); + } + else { + // Delete the surrounding trivia because it will have been retained in newImportDecls. + const replaceOptions = { + useNonAdjustedStartPosition: false, + useNonAdjustedEndPosition: false, + suffix: getNewLineOrDefaultFromHost(host, formatContext.options), + }; + changeTracker.replaceNodeWithNodes(sourceFile, oldImportDecls[0], newImportDecls, replaceOptions); + } + + const changes = changeTracker.getChanges(); + return changes; + } + + function removeUnusedImports(oldImports: ReadonlyArray) { + return oldImports; // TODO (https://github.com/Microsoft/TypeScript/issues/10020) + } + + /* @internal */ // Internal for testing + export function sortImports(oldImports: ReadonlyArray) { + if (oldImports.length < 2) { + return oldImports; + } + + // NB: declaration order determines sort order + const enum ModuleNameKind { + NonRelative, + Relative, + Invalid, + } + + const importRecords = oldImports.map(createImportRecord); + + const sortedRecords = stableSort(importRecords, (import1, import2) => { + const { name: name1, kind: kind1 } = import1; + const { name: name2, kind: kind2 } = import2; + + if (kind1 !== kind2) { + return kind1 < kind2 + ? Comparison.LessThan + : Comparison.GreaterThan; + } + + // Note that we're using simple equality, retaining case-sensitivity. + if (name1 !== name2) { + return name1 < name2 + ? Comparison.LessThan + : Comparison.GreaterThan; + } + + return Comparison.EqualTo; + }); + + return sortedRecords.map(r => r.importDeclaration); + + function createImportRecord(importDeclaration: ImportDeclaration) { + const specifier = importDeclaration.moduleSpecifier; + const name = getExternalModuleName(specifier); + if (name) { + const isRelative = isExternalModuleNameRelative(name); + return { importDeclaration, name, kind: isRelative ? ModuleNameKind.Relative : ModuleNameKind.NonRelative }; + } + + return { importDeclaration, name: specifier.getText(), kind: ModuleNameKind.Invalid }; + } + } + + function getExternalModuleName(specifier: Expression) { + return isStringLiteral(specifier) || isNoSubstitutionTemplateLiteral(specifier) + ? specifier.text + : undefined; + } + + /** + * @param sortedImports a non-empty list of ImportDeclarations, sorted by module name. + */ + function groupSortedImports(sortedImports: ReadonlyArray): ReadonlyArray> { + Debug.assert(length(sortedImports) > 0); + + const groups: ImportDeclaration[][] = []; + + let groupName: string | undefined = getExternalModuleName(sortedImports[0].moduleSpecifier); + let group: ImportDeclaration[] = []; + + for (const importDeclaration of sortedImports) { + const moduleName = getExternalModuleName(importDeclaration.moduleSpecifier); + if (moduleName && moduleName === groupName) { + group.push(importDeclaration); + } + else if (group.length) { + groups.push(group); + + groupName = moduleName; + group = [importDeclaration]; + } + } + + if (group.length) { + groups.push(group); + } + + return groups; + } + + /* @internal */ // Internal for testing + /** + * @param sortedImports a list of ImportDeclarations, sorted by module name. + */ + export function coalesceImports(sortedImports: ReadonlyArray) { + if (sortedImports.length === 0) { + return sortedImports; + } + + const coalescedImports: ImportDeclaration[] = []; + + const groupedImports = groupSortedImports(sortedImports); + for (const importGroup of groupedImports) { + + let seenImportWithoutClause = false; + + const defaultImports: Identifier[] = []; + const namespaceImports: NamespaceImport[] = []; + const namedImports: NamedImports[] = []; + + for (const importDeclaration of importGroup) { + if (importDeclaration.importClause === undefined) { + // Only the first such import is interesting - the others are redundant. + // Note: Unfortunately, we will lose trivia that was on this node. + if (!seenImportWithoutClause) { + coalescedImports.push(importDeclaration); + } + + seenImportWithoutClause = true; + continue; + } + + const { name, namedBindings } = importDeclaration.importClause; + + if (name) { + defaultImports.push(name); + } + + if (namedBindings) { + if (isNamespaceImport(namedBindings)) { + namespaceImports.push(namedBindings); + } + else { + namedImports.push(namedBindings); + } + } + } + + // Normally, we don't combine default and namespace imports, but it would be silly to + // produce two import declarations in this special case. + if (defaultImports.length === 1 && namespaceImports.length === 1 && namedImports.length === 0) { + // Add the namespace import to the existing default ImportDeclaration. + const defaultImportClause = defaultImports[0].parent as ImportClause; + coalescedImports.push( + updateImportDeclarationAndClause(defaultImportClause, defaultImportClause.name, namespaceImports[0])); + + continue; + } + + // For convenience, we cheat and do a little sorting during coalescing. + // Seems reasonable since we're restructuring so much anyway. + const sortedNamespaceImports = stableSort(namespaceImports, (n1, n2) => compareIdentifiers(n1.name, n2.name)); + + for (const namespaceImport of sortedNamespaceImports) { + // Drop the name, if any + coalescedImports.push( + updateImportDeclarationAndClause(namespaceImport.parent, /*name*/ undefined, namespaceImport)); + } + + if (defaultImports.length === 0 && namedImports.length === 0) { + continue; + } + + let newDefaultImport: Identifier = undefined; + const newImportSpecifiers: ImportSpecifier[] = []; + if (defaultImports.length === 1) { + newDefaultImport = defaultImports[0]; + } + else { + for (const defaultImport of defaultImports) { + newImportSpecifiers.push( + createImportSpecifier(createIdentifier("default"), defaultImport)); + } + } + + for (const namedImport of namedImports) { + for (const specifier of namedImport.elements) { + newImportSpecifiers.push(specifier); + } + } + + const sortedImportSpecifiers = stableSort(newImportSpecifiers, (s1, s2) => { + const nameComparison = compareIdentifiers(s1.propertyName || s1.name, s2.propertyName || s2.name); + return nameComparison !== Comparison.EqualTo + ? nameComparison + : compareIdentifiers(s1.name, s2.name); + }); + + const importClause = defaultImports.length > 0 + ? defaultImports[0].parent as ImportClause + : namedImports[0].parent; + + const newNamedImports = sortedImportSpecifiers.length === 0 + ? undefined + : namedImports.length === 0 + ? createNamedImports(sortedImportSpecifiers) + : updateNamedImports(namedImports[0], sortedImportSpecifiers); + + coalescedImports.push( + updateImportDeclarationAndClause(importClause, newDefaultImport, newNamedImports)); + } + + return coalescedImports; + + // `undefined` is the min value. + function compareIdentifiers(s1: Identifier | undefined, s2: Identifier | undefined) { + return s1 === undefined + ? s2 === undefined + ? Comparison.EqualTo + : Comparison.LessThan + : s2 === undefined + ? Comparison.GreaterThan + : s1.text < s2.text + ? Comparison.LessThan + : s1.text > s2.text + ? Comparison.GreaterThan + : Comparison.EqualTo; + } + + function updateImportDeclarationAndClause( + importClause: ImportClause, + name: Identifier | undefined, + namedBindings: NamedImportBindings | undefined) { + + const importDeclaration = importClause.parent; + return updateImportDeclaration( + importDeclaration, + importDeclaration.decorators, + importDeclaration.modifiers, + updateImportClause(importClause, name, namedBindings), + importDeclaration.moduleSpecifier); + } + } +} \ No newline at end of file diff --git a/src/services/services.ts b/src/services/services.ts index 3b984c2e159..37cc192442f 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -14,6 +14,7 @@ /// /// /// +/// /// /// /// @@ -1854,50 +1855,7 @@ namespace ts { const sourceFile = getValidSourceFile(scope.fileName); const formatContext = formatting.getFormatContext(formatOptions); - // All of the (old) ImportDeclarations in the file, in syntactic order. - const oldImportDecls: ImportDeclaration[] = []; - - forEachChild(sourceFile, node => { - cancellationToken.throwIfCancellationRequested(); - if (isImportDeclaration(node)) { - oldImportDecls.push(node); - } - // TODO (https://github.com/Microsoft/TypeScript/issues/10020): sort *within* ambient modules (find using isAmbientModule) - }); - - if (oldImportDecls.length === 0) { - return []; - } - - const usedImportDecls = removeUnusedImports(oldImportDecls); - const sortedImportDecls = sortImports(usedImportDecls); - const coalescedImportDecls = coalesceImports(sortedImportDecls); - - // All of the (new) ImportDeclarations in the file, in sorted order. - const newImportDecls = coalescedImportDecls; - - const changeTracker = textChanges.ChangeTracker.fromContext({ host, formatContext }); - - // NB: Stopping before i === 0 - for (let i = oldImportDecls.length - 1; i > 0; i--) { - changeTracker.deleteNode(sourceFile, oldImportDecls[i]); - } - - if (newImportDecls.length === 0) { - changeTracker.deleteNode(sourceFile, oldImportDecls[0]); - } - else { - // Delete the surrounding trivia because it will have been retained in newImportDecls. - const replaceOptions = { - useNonAdjustedStartPosition: false, - useNonAdjustedEndPosition: false, - suffix: getNewLineOrDefaultFromHost(host, formatOptions), - }; - changeTracker.replaceNodeWithNodes(sourceFile, oldImportDecls[0], newImportDecls, replaceOptions); - } - - const changes = changeTracker.getChanges(); - return changes; + return OrganizeImports.organizeImports(sourceFile, formatContext, host, cancellationToken); } function applyCodeActionCommand(action: CodeActionCommand): Promise; @@ -2321,238 +2279,4 @@ namespace ts { } objectAllocator = getServicesObjectAllocator(); - - function removeUnusedImports(oldImports: ReadonlyArray) { - return oldImports; // TODO (https://github.com/Microsoft/TypeScript/issues/10020) - } - - /* @internal */ // Internal for testing - export function sortImports(oldImports: ReadonlyArray) { - if (oldImports.length < 2) { - return oldImports; - } - - // NB: declaration order determines sort order - const enum ModuleNameKind { - NonRelative, - Relative, - Invalid, - } - - const importRecords = oldImports.map(createImportRecord); - - const sortedRecords = stableSort(importRecords, (import1, import2) => { - const { name: name1, kind: kind1 } = import1; - const { name: name2, kind: kind2 } = import2; - - if (kind1 !== kind2) { - return kind1 < kind2 - ? Comparison.LessThan - : Comparison.GreaterThan; - } - - // Note that we're using simple equality, retaining case-sensitivity. - if (name1 !== name2) { - return name1 < name2 - ? Comparison.LessThan - : Comparison.GreaterThan; - } - - return Comparison.EqualTo; - }); - - return sortedRecords.map(r => r.importDeclaration); - - function createImportRecord(importDeclaration: ImportDeclaration) { - const specifier = importDeclaration.moduleSpecifier; - const name = getExternalModuleName(specifier); - if (name) { - const isRelative = isExternalModuleNameRelative(name); - return { importDeclaration, name, kind: isRelative ? ModuleNameKind.Relative : ModuleNameKind.NonRelative }; - } - - return { importDeclaration, name: specifier.getText(), kind: ModuleNameKind.Invalid }; - } - } - - function getExternalModuleName(specifier: Expression) { - return isStringLiteral(specifier) || isNoSubstitutionTemplateLiteral(specifier) - ? specifier.text - : undefined; - } - - /** - * @param sortedImports a non-empty list of ImportDeclarations, sorted by module name. - */ - function groupSortedImports(sortedImports: ReadonlyArray): ReadonlyArray> { - Debug.assert(length(sortedImports) > 0); - - const groups: ImportDeclaration[][] = []; - - let groupName: string | undefined = getExternalModuleName(sortedImports[0].moduleSpecifier); - let group: ImportDeclaration[] = []; - - for (const importDeclaration of sortedImports) { - const moduleName = getExternalModuleName(importDeclaration.moduleSpecifier); - if (moduleName && moduleName === groupName) { - group.push(importDeclaration); - } - else if (group.length) { - groups.push(group); - - groupName = moduleName; - group = [importDeclaration]; - } - } - - if (group.length) { - groups.push(group); - } - - return groups; - } - - /* @internal */ // Internal for testing - /** - * @param sortedImports a list of ImportDeclarations, sorted by module name. - */ - export function coalesceImports(sortedImports: ReadonlyArray) { - if (sortedImports.length === 0) { - return sortedImports; - } - - const coalescedImports: ImportDeclaration[] = []; - - const groupedImports = groupSortedImports(sortedImports); - for (const importGroup of groupedImports) { - - let seenImportWithoutClause = false; - - const defaultImports: Identifier[] = []; - const namespaceImports: NamespaceImport[] = []; - const namedImports: NamedImports[] = []; - - for (const importDeclaration of importGroup) { - if (importDeclaration.importClause === undefined) { - // Only the first such import is interesting - the others are redundant. - // Note: Unfortunately, we will lose trivia that was on this node. - if (!seenImportWithoutClause) { - coalescedImports.push(importDeclaration); - } - - seenImportWithoutClause = true; - continue; - } - - const { name, namedBindings } = importDeclaration.importClause; - - if (name) { - defaultImports.push(name); - } - - if (namedBindings) { - if (isNamespaceImport(namedBindings)) { - namespaceImports.push(namedBindings); - } - else { - namedImports.push(namedBindings); - } - } - } - - // Normally, we don't combine default and namespace imports, but it would be silly to - // produce two import declarations in this special case. - if (defaultImports.length === 1 && namespaceImports.length === 1 && namedImports.length === 0) { - // Add the namespace import to the existing default ImportDeclaration. - const defaultImportClause = defaultImports[0].parent as ImportClause; - coalescedImports.push( - updateImportDeclarationAndClause(defaultImportClause, defaultImportClause.name, namespaceImports[0])); - - continue; - } - - // For convenience, we cheat and do a little sorting during coalescing. - // Seems reasonable since we're restructuring so much anyway. - const sortedNamespaceImports = stableSort(namespaceImports, (n1, n2) => compareIdentifiers(n1.name, n2.name)); - - for (const namespaceImport of sortedNamespaceImports) { - // Drop the name, if any - coalescedImports.push( - updateImportDeclarationAndClause(namespaceImport.parent, /*name*/ undefined, namespaceImport)); - } - - if (defaultImports.length === 0 && namedImports.length === 0) { - continue; - } - - let newDefaultImport: Identifier = undefined; - const newImportSpecifiers: ImportSpecifier[] = []; - if (defaultImports.length === 1) { - newDefaultImport = defaultImports[0]; - } - else { - for (const defaultImport of defaultImports) { - newImportSpecifiers.push( - createImportSpecifier(createIdentifier("default"), defaultImport)); - } - } - - for (const namedImport of namedImports) { - for (const specifier of namedImport.elements) { - newImportSpecifiers.push(specifier); - } - } - - const sortedImportSpecifiers = stableSort(newImportSpecifiers, (s1, s2) => { - const nameComparison = compareIdentifiers(s1.propertyName || s1.name, s2.propertyName || s2.name); - return nameComparison !== Comparison.EqualTo - ? nameComparison - : compareIdentifiers(s1.name, s2.name); - }); - - const importClause = defaultImports.length > 0 - ? defaultImports[0].parent as ImportClause - : namedImports[0].parent; - - const newNamedImports = sortedImportSpecifiers.length === 0 - ? undefined - : namedImports.length === 0 - ? createNamedImports(sortedImportSpecifiers) - : updateNamedImports(namedImports[0], sortedImportSpecifiers); - - coalescedImports.push( - updateImportDeclarationAndClause(importClause, newDefaultImport, newNamedImports)); - } - - return coalescedImports; - - // `undefined` is the min value. - function compareIdentifiers(s1: Identifier | undefined, s2: Identifier | undefined) { - return s1 === undefined - ? s2 === undefined - ? Comparison.EqualTo - : Comparison.LessThan - : s2 === undefined - ? Comparison.GreaterThan - : s1.text < s2.text - ? Comparison.LessThan - : s1.text > s2.text - ? Comparison.GreaterThan - : Comparison.EqualTo; - } - - function updateImportDeclarationAndClause( - importClause: ImportClause, - name: Identifier | undefined, - namedBindings: NamedImportBindings | undefined) { - - const importDeclaration = importClause.parent; - return updateImportDeclaration( - importDeclaration, - importDeclaration.decorators, - importDeclaration.modifiers, - updateImportClause(importClause, name, namedBindings), - importDeclaration.moduleSpecifier); - } - } }