Separate OrganizeImports into its own namespace and file

This commit is contained in:
Andrew Casey
2018-02-13 14:52:15 -08:00
parent 979b14689e
commit f4141ac6bf
3 changed files with 314 additions and 299 deletions

View File

@@ -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);
});

View File

@@ -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<ImportDeclaration>) {
return oldImports; // TODO (https://github.com/Microsoft/TypeScript/issues/10020)
}
/* @internal */ // Internal for testing
export function sortImports(oldImports: ReadonlyArray<ImportDeclaration>) {
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<ImportDeclaration>): ReadonlyArray<ReadonlyArray<ImportDeclaration>> {
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<ImportDeclaration>) {
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);
}
}
}

View File

@@ -14,6 +14,7 @@
/// <reference path='jsTyping.ts' />
/// <reference path='navigateTo.ts' />
/// <reference path='navigationBar.ts' />
/// <reference path='organizeImports.ts' />
/// <reference path='outliningElementsCollector.ts' />
/// <reference path='patternMatcher.ts' />
/// <reference path='preProcess.ts' />
@@ -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<ApplyCodeActionCommandResult>;
@@ -2321,238 +2279,4 @@ namespace ts {
}
objectAllocator = getServicesObjectAllocator();
function removeUnusedImports(oldImports: ReadonlyArray<ImportDeclaration>) {
return oldImports; // TODO (https://github.com/Microsoft/TypeScript/issues/10020)
}
/* @internal */ // Internal for testing
export function sortImports(oldImports: ReadonlyArray<ImportDeclaration>) {
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<ImportDeclaration>): ReadonlyArray<ReadonlyArray<ImportDeclaration>> {
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<ImportDeclaration>) {
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);
}
}
}