Merge pull request #24237 from amcasey/GH23640
Sort exports when organizeImports is run
This commit is contained in:
commit
04a351224c
|
@ -44,13 +44,6 @@ namespace ts {
|
||||||
assert.isEmpty(OrganizeImports.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 = OrganizeImports.coalesceImports(sortedImports);
|
|
||||||
const expectedCoalescedImports = parseImports(`import { a as n, b, default as m, y, z as o } from "lib";`);
|
|
||||||
assertListEqual(actualCoalescedImports, expectedCoalescedImports);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("Sort specifiers - case-insensitive", () => {
|
it("Sort specifiers - case-insensitive", () => {
|
||||||
const sortedImports = parseImports(`import { default as M, a as n, B, y, Z as O } from "lib";`);
|
const sortedImports = parseImports(`import { default as M, a as n, B, y, Z as O } from "lib";`);
|
||||||
const actualCoalescedImports = OrganizeImports.coalesceImports(sortedImports);
|
const actualCoalescedImports = OrganizeImports.coalesceImports(sortedImports);
|
||||||
|
@ -181,6 +174,78 @@ namespace ts {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("Coalesce exports", () => {
|
||||||
|
it("No exports", () => {
|
||||||
|
assert.isEmpty(OrganizeImports.coalesceExports([]));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Sort specifiers - case-insensitive", () => {
|
||||||
|
const sortedExports = parseExports(`export { default as M, a as n, B, y, Z as O } from "lib";`);
|
||||||
|
const actualCoalescedExports = OrganizeImports.coalesceExports(sortedExports);
|
||||||
|
const expectedCoalescedExports = parseExports(`export { a as n, B, default as M, y, Z as O } from "lib";`);
|
||||||
|
assertListEqual(actualCoalescedExports, expectedCoalescedExports);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Combine namespace re-exports", () => {
|
||||||
|
const sortedExports = parseExports(
|
||||||
|
`export * from "lib";`,
|
||||||
|
`export * from "lib";`);
|
||||||
|
const actualCoalescedExports = OrganizeImports.coalesceExports(sortedExports);
|
||||||
|
const expectedCoalescedExports = parseExports(`export * from "lib";`);
|
||||||
|
assertListEqual(actualCoalescedExports, expectedCoalescedExports);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Combine property exports", () => {
|
||||||
|
const sortedExports = parseExports(
|
||||||
|
`export { x };`,
|
||||||
|
`export { y as z };`);
|
||||||
|
const actualCoalescedExports = OrganizeImports.coalesceExports(sortedExports);
|
||||||
|
const expectedCoalescedExports = parseExports(`export { x, y as z };`);
|
||||||
|
assertListEqual(actualCoalescedExports, expectedCoalescedExports);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Combine property re-exports", () => {
|
||||||
|
const sortedExports = parseExports(
|
||||||
|
`export { x } from "lib";`,
|
||||||
|
`export { y as z } from "lib";`);
|
||||||
|
const actualCoalescedExports = OrganizeImports.coalesceExports(sortedExports);
|
||||||
|
const expectedCoalescedExports = parseExports(`export { x, y as z } from "lib";`);
|
||||||
|
assertListEqual(actualCoalescedExports, expectedCoalescedExports);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Combine namespace re-export with property re-export", () => {
|
||||||
|
const sortedExports = parseExports(
|
||||||
|
`export * from "lib";`,
|
||||||
|
`export { y } from "lib";`);
|
||||||
|
const actualCoalescedExports = OrganizeImports.coalesceExports(sortedExports);
|
||||||
|
const expectedCoalescedExports = sortedExports;
|
||||||
|
assertListEqual(actualCoalescedExports, expectedCoalescedExports);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Combine many exports", () => {
|
||||||
|
const sortedExports = parseExports(
|
||||||
|
`export { x };`,
|
||||||
|
`export { y as w, z as default };`,
|
||||||
|
`export { w as q };`);
|
||||||
|
const actualCoalescedExports = OrganizeImports.coalesceExports(sortedExports);
|
||||||
|
const expectedCoalescedExports = parseExports(
|
||||||
|
`export { w as q, x, y as w, z as default };`);
|
||||||
|
assertListEqual(actualCoalescedExports, expectedCoalescedExports);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Combine many re-exports", () => {
|
||||||
|
const sortedExports = parseExports(
|
||||||
|
`export { x as a, y } from "lib";`,
|
||||||
|
`export * from "lib";`,
|
||||||
|
`export { z as b } from "lib";`);
|
||||||
|
const actualCoalescedExports = OrganizeImports.coalesceExports(sortedExports);
|
||||||
|
const expectedCoalescedExports = parseExports(
|
||||||
|
`export * from "lib";`,
|
||||||
|
`export { x as a, y, z as b } from "lib";`);
|
||||||
|
assertListEqual(actualCoalescedExports, expectedCoalescedExports);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe("Baselines", () => {
|
describe("Baselines", () => {
|
||||||
|
|
||||||
const libFile = {
|
const libFile = {
|
||||||
|
@ -471,6 +536,154 @@ import { React, Other } from "react";
|
||||||
},
|
},
|
||||||
reactLibFile);
|
reactLibFile);
|
||||||
|
|
||||||
|
describe("Exports", () => {
|
||||||
|
|
||||||
|
testOrganizeExports("MoveToTop",
|
||||||
|
{
|
||||||
|
path: "/test.ts",
|
||||||
|
content: `
|
||||||
|
export { F1, F2 } from "lib";
|
||||||
|
1;
|
||||||
|
export * from "lib";
|
||||||
|
2;
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
libFile);
|
||||||
|
|
||||||
|
// tslint:disable no-invalid-template-strings
|
||||||
|
testOrganizeExports("MoveToTop_Invalid",
|
||||||
|
{
|
||||||
|
path: "/test.ts",
|
||||||
|
content: `
|
||||||
|
export { F1, F2 } from "lib";
|
||||||
|
1;
|
||||||
|
export * from "lib";
|
||||||
|
2;
|
||||||
|
export { b } from ${"`${'lib'}`"};
|
||||||
|
export { a } from ${"`${'lib'}`"};
|
||||||
|
export { D } from "lib";
|
||||||
|
3;
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
libFile);
|
||||||
|
// tslint:enable no-invalid-template-strings
|
||||||
|
|
||||||
|
testOrganizeExports("MoveToTop_WithImportsFirst",
|
||||||
|
{
|
||||||
|
path: "/test.ts",
|
||||||
|
content: `
|
||||||
|
import { F1, F2 } from "lib";
|
||||||
|
1;
|
||||||
|
export { F1, F2 } from "lib";
|
||||||
|
2;
|
||||||
|
import * as NS from "lib";
|
||||||
|
3;
|
||||||
|
export * from "lib";
|
||||||
|
4;
|
||||||
|
F1(); F2(); NS.F1();
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
libFile);
|
||||||
|
|
||||||
|
testOrganizeExports("MoveToTop_WithExportsFirst",
|
||||||
|
{
|
||||||
|
path: "/test.ts",
|
||||||
|
content: `
|
||||||
|
export { F1, F2 } from "lib";
|
||||||
|
1;
|
||||||
|
import { F1, F2 } from "lib";
|
||||||
|
2;
|
||||||
|
export * from "lib";
|
||||||
|
3;
|
||||||
|
import * as NS from "lib";
|
||||||
|
4;
|
||||||
|
F1(); F2(); NS.F1();
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
libFile);
|
||||||
|
|
||||||
|
testOrganizeExports("CoalesceMultipleModules",
|
||||||
|
{
|
||||||
|
path: "/test.ts",
|
||||||
|
content: `
|
||||||
|
export { d } from "lib1";
|
||||||
|
export { b } from "lib1";
|
||||||
|
export { c } from "lib2";
|
||||||
|
export { a } from "lib2";
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{ path: "/lib1.ts", content: "export const b = 1, d = 2;" },
|
||||||
|
{ path: "/lib2.ts", content: "export const a = 3, c = 4;" });
|
||||||
|
|
||||||
|
testOrganizeExports("CoalesceTrivia",
|
||||||
|
{
|
||||||
|
path: "/test.ts",
|
||||||
|
content: `
|
||||||
|
/*A*/export /*B*/ { /*C*/ F2 /*D*/ } /*E*/ from /*F*/ "lib" /*G*/;/*H*/ //I
|
||||||
|
/*J*/export /*K*/ { /*L*/ F1 /*M*/ } /*N*/ from /*O*/ "lib" /*P*/;/*Q*/ //R
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
libFile);
|
||||||
|
|
||||||
|
testOrganizeExports("SortTrivia",
|
||||||
|
{
|
||||||
|
path: "/test.ts",
|
||||||
|
content: `
|
||||||
|
/*A*/export /*B*/ * /*C*/ from /*D*/ "lib2" /*E*/;/*F*/ //G
|
||||||
|
/*H*/export /*I*/ * /*J*/ from /*K*/ "lib1" /*L*/;/*M*/ //N
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{ path: "/lib1.ts", content: "" },
|
||||||
|
{ path: "/lib2.ts", content: "" });
|
||||||
|
|
||||||
|
testOrganizeExports("SortHeaderComment",
|
||||||
|
{
|
||||||
|
path: "/test.ts",
|
||||||
|
content: `
|
||||||
|
// Header
|
||||||
|
export * from "lib2";
|
||||||
|
export * from "lib1";
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{ path: "/lib1.ts", content: "" },
|
||||||
|
{ path: "/lib2.ts", content: "" });
|
||||||
|
|
||||||
|
testOrganizeExports("AmbientModule",
|
||||||
|
{
|
||||||
|
path: "/test.ts",
|
||||||
|
content: `
|
||||||
|
declare module "mod" {
|
||||||
|
export { F1 } from "lib";
|
||||||
|
export * from "lib";
|
||||||
|
export { F2 } from "lib";
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
libFile);
|
||||||
|
|
||||||
|
testOrganizeExports("TopLevelAndAmbientModule",
|
||||||
|
{
|
||||||
|
path: "/test.ts",
|
||||||
|
content: `
|
||||||
|
export { D } from "lib";
|
||||||
|
|
||||||
|
declare module "mod" {
|
||||||
|
export { F1 } from "lib";
|
||||||
|
export * from "lib";
|
||||||
|
export { F2 } from "lib";
|
||||||
|
}
|
||||||
|
|
||||||
|
export { E } from "lib";
|
||||||
|
export * from "lib";
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
libFile);
|
||||||
|
});
|
||||||
|
|
||||||
|
function testOrganizeExports(testName: string, testFile: TestFSWithWatch.File, ...otherFiles: TestFSWithWatch.File[]) {
|
||||||
|
testOrganizeImports(`${testName}.exports`, testFile, ...otherFiles);
|
||||||
|
}
|
||||||
|
|
||||||
function testOrganizeImports(testName: string, testFile: TestFSWithWatch.File, ...otherFiles: TestFSWithWatch.File[]) {
|
function testOrganizeImports(testName: string, testFile: TestFSWithWatch.File, ...otherFiles: TestFSWithWatch.File[]) {
|
||||||
it(testName, () => runBaseline(`organizeImports/${testName}.ts`, testFile, ...otherFiles));
|
it(testName, () => runBaseline(`organizeImports/${testName}.ts`, testFile, ...otherFiles));
|
||||||
}
|
}
|
||||||
|
@ -509,6 +722,13 @@ import { React, Other } from "react";
|
||||||
return imports;
|
return imports;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function parseExports(...exportStrings: string[]): ReadonlyArray<ExportDeclaration> {
|
||||||
|
const sourceFile = createSourceFile("a.ts", exportStrings.join("\n"), ScriptTarget.ES2015, /*setParentNodes*/ true, ScriptKind.TS);
|
||||||
|
const exports = filter(sourceFile.statements, isExportDeclaration);
|
||||||
|
assert.equal(exports.length, exportStrings.length);
|
||||||
|
return exports;
|
||||||
|
}
|
||||||
|
|
||||||
function assertEqual(node1?: Node, node2?: Node) {
|
function assertEqual(node1?: Node, node2?: Node) {
|
||||||
if (node1 === undefined) {
|
if (node1 === undefined) {
|
||||||
assert.isUndefined(node2);
|
assert.isUndefined(node2);
|
||||||
|
@ -550,6 +770,23 @@ import { React, Other } from "react";
|
||||||
assertEqual(is1.name, is2.name);
|
assertEqual(is1.name, is2.name);
|
||||||
assertEqual(is1.propertyName, is2.propertyName);
|
assertEqual(is1.propertyName, is2.propertyName);
|
||||||
break;
|
break;
|
||||||
|
case SyntaxKind.ExportDeclaration:
|
||||||
|
const ed1 = node1 as ExportDeclaration;
|
||||||
|
const ed2 = node2 as ExportDeclaration;
|
||||||
|
assertEqual(ed1.exportClause, ed2.exportClause);
|
||||||
|
assertEqual(ed1.moduleSpecifier, ed2.moduleSpecifier);
|
||||||
|
break;
|
||||||
|
case SyntaxKind.NamedExports:
|
||||||
|
const ne1 = node1 as NamedExports;
|
||||||
|
const ne2 = node2 as NamedExports;
|
||||||
|
assertListEqual(ne1.elements, ne2.elements);
|
||||||
|
break;
|
||||||
|
case SyntaxKind.ExportSpecifier:
|
||||||
|
const es1 = node1 as ExportSpecifier;
|
||||||
|
const es2 = node2 as ExportSpecifier;
|
||||||
|
assertEqual(es1.name, es2.name);
|
||||||
|
assertEqual(es1.propertyName, es2.propertyName);
|
||||||
|
break;
|
||||||
case SyntaxKind.Identifier:
|
case SyntaxKind.Identifier:
|
||||||
const id1 = node1 as Identifier;
|
const id1 = node1 as Identifier;
|
||||||
const id2 = node2 as Identifier;
|
const id2 = node2 as Identifier;
|
||||||
|
|
|
@ -17,19 +17,32 @@ namespace ts.OrganizeImports {
|
||||||
|
|
||||||
const changeTracker = textChanges.ChangeTracker.fromContext({ host, formatContext });
|
const changeTracker = textChanges.ChangeTracker.fromContext({ host, formatContext });
|
||||||
|
|
||||||
|
const coalesceAndOrganizeImports = (importGroup: ReadonlyArray<ImportDeclaration>) => coalesceImports(removeUnusedImports(importGroup, sourceFile, program));
|
||||||
|
|
||||||
// All of the old ImportDeclarations in the file, in syntactic order.
|
// All of the old ImportDeclarations in the file, in syntactic order.
|
||||||
const topLevelImportDecls = sourceFile.statements.filter(isImportDeclaration);
|
const topLevelImportDecls = sourceFile.statements.filter(isImportDeclaration);
|
||||||
organizeImportsWorker(topLevelImportDecls);
|
organizeImportsWorker(topLevelImportDecls, coalesceAndOrganizeImports);
|
||||||
|
|
||||||
|
// All of the old ExportDeclarations in the file, in syntactic order.
|
||||||
|
const topLevelExportDecls = sourceFile.statements.filter(isExportDeclaration);
|
||||||
|
organizeImportsWorker(topLevelExportDecls, coalesceExports);
|
||||||
|
|
||||||
for (const ambientModule of sourceFile.statements.filter(isAmbientModule)) {
|
for (const ambientModule of sourceFile.statements.filter(isAmbientModule)) {
|
||||||
const ambientModuleBody = getModuleBlock(ambientModule as ModuleDeclaration);
|
const ambientModuleBody = getModuleBlock(ambientModule as ModuleDeclaration);
|
||||||
|
|
||||||
const ambientModuleImportDecls = ambientModuleBody.statements.filter(isImportDeclaration);
|
const ambientModuleImportDecls = ambientModuleBody.statements.filter(isImportDeclaration);
|
||||||
organizeImportsWorker(ambientModuleImportDecls);
|
organizeImportsWorker(ambientModuleImportDecls, coalesceAndOrganizeImports);
|
||||||
|
|
||||||
|
const ambientModuleExportDecls = ambientModuleBody.statements.filter(isExportDeclaration);
|
||||||
|
organizeImportsWorker(ambientModuleExportDecls, coalesceExports);
|
||||||
}
|
}
|
||||||
|
|
||||||
return changeTracker.getChanges();
|
return changeTracker.getChanges();
|
||||||
|
|
||||||
function organizeImportsWorker(oldImportDecls: ReadonlyArray<ImportDeclaration>) {
|
function organizeImportsWorker<T extends ImportDeclaration | ExportDeclaration>(
|
||||||
|
oldImportDecls: ReadonlyArray<T>,
|
||||||
|
coalesce: (group: ReadonlyArray<T>) => ReadonlyArray<T>) {
|
||||||
|
|
||||||
if (length(oldImportDecls) === 0) {
|
if (length(oldImportDecls) === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -45,7 +58,7 @@ namespace ts.OrganizeImports {
|
||||||
const sortedImportGroups = stableSort(oldImportGroups, (group1, group2) => compareModuleSpecifiers(group1[0].moduleSpecifier, group2[0].moduleSpecifier));
|
const sortedImportGroups = stableSort(oldImportGroups, (group1, group2) => compareModuleSpecifiers(group1[0].moduleSpecifier, group2[0].moduleSpecifier));
|
||||||
const newImportDecls = flatMap(sortedImportGroups, importGroup =>
|
const newImportDecls = flatMap(sortedImportGroups, importGroup =>
|
||||||
getExternalModuleName(importGroup[0].moduleSpecifier)
|
getExternalModuleName(importGroup[0].moduleSpecifier)
|
||||||
? coalesceImports(removeUnusedImports(importGroup, sourceFile, program))
|
? coalesce(importGroup)
|
||||||
: importGroup);
|
: importGroup);
|
||||||
|
|
||||||
// Delete or replace the first import.
|
// Delete or replace the first import.
|
||||||
|
@ -131,7 +144,9 @@ namespace ts.OrganizeImports {
|
||||||
}
|
}
|
||||||
|
|
||||||
function getExternalModuleName(specifier: Expression) {
|
function getExternalModuleName(specifier: Expression) {
|
||||||
return isStringLiteralLike(specifier) ? specifier.text : undefined;
|
return specifier !== undefined && isStringLiteralLike(specifier)
|
||||||
|
? specifier.text
|
||||||
|
: undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* @internal */ // Internal for testing
|
/* @internal */ // Internal for testing
|
||||||
|
@ -189,9 +204,7 @@ namespace ts.OrganizeImports {
|
||||||
|
|
||||||
newImportSpecifiers.push(...flatMap(namedImports, i => (i.importClause.namedBindings as NamedImports).elements));
|
newImportSpecifiers.push(...flatMap(namedImports, i => (i.importClause.namedBindings as NamedImports).elements));
|
||||||
|
|
||||||
const sortedImportSpecifiers = stableSort(newImportSpecifiers, (s1, s2) =>
|
const sortedImportSpecifiers = sortSpecifiers(newImportSpecifiers);
|
||||||
compareIdentifiers(s1.propertyName || s1.name, s2.propertyName || s2.name) ||
|
|
||||||
compareIdentifiers(s1.name, s2.name));
|
|
||||||
|
|
||||||
const importDecl = defaultImports.length > 0
|
const importDecl = defaultImports.length > 0
|
||||||
? defaultImports[0]
|
? defaultImports[0]
|
||||||
|
@ -254,9 +267,69 @@ namespace ts.OrganizeImports {
|
||||||
namedImports,
|
namedImports,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function compareIdentifiers(s1: Identifier, s2: Identifier) {
|
/* @internal */ // Internal for testing
|
||||||
return compareStringsCaseInsensitive(s1.text, s2.text);
|
/**
|
||||||
|
* @param exportGroup a list of ExportDeclarations, all with the same module name.
|
||||||
|
*/
|
||||||
|
export function coalesceExports(exportGroup: ReadonlyArray<ExportDeclaration>) {
|
||||||
|
if (exportGroup.length === 0) {
|
||||||
|
return exportGroup;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { exportWithoutClause, namedExports } = getCategorizedExports(exportGroup);
|
||||||
|
|
||||||
|
const coalescedExports: ExportDeclaration[] = [];
|
||||||
|
|
||||||
|
if (exportWithoutClause) {
|
||||||
|
coalescedExports.push(exportWithoutClause);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (namedExports.length === 0) {
|
||||||
|
return coalescedExports;
|
||||||
|
}
|
||||||
|
|
||||||
|
const newExportSpecifiers: ExportSpecifier[] = [];
|
||||||
|
newExportSpecifiers.push(...flatMap(namedExports, i => (i.exportClause).elements));
|
||||||
|
|
||||||
|
const sortedExportSpecifiers = sortSpecifiers(newExportSpecifiers);
|
||||||
|
|
||||||
|
const exportDecl = namedExports[0];
|
||||||
|
coalescedExports.push(
|
||||||
|
updateExportDeclaration(
|
||||||
|
exportDecl,
|
||||||
|
exportDecl.decorators,
|
||||||
|
exportDecl.modifiers,
|
||||||
|
updateNamedExports(exportDecl.exportClause, sortedExportSpecifiers),
|
||||||
|
exportDecl.moduleSpecifier));
|
||||||
|
|
||||||
|
return coalescedExports;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns entire export declarations because they may already have been rewritten and
|
||||||
|
* may lack parent pointers. The desired parts can easily be recovered based on the
|
||||||
|
* categorization.
|
||||||
|
*/
|
||||||
|
function getCategorizedExports(exportGroup: ReadonlyArray<ExportDeclaration>) {
|
||||||
|
let exportWithoutClause: ExportDeclaration | undefined;
|
||||||
|
const namedExports: ExportDeclaration[] = [];
|
||||||
|
|
||||||
|
for (const exportDeclaration of exportGroup) {
|
||||||
|
if (exportDeclaration.exportClause === undefined) {
|
||||||
|
// Only the first such export is interesting - the others are redundant.
|
||||||
|
// Note: Unfortunately, we will lose trivia that was on this node.
|
||||||
|
exportWithoutClause = exportWithoutClause || exportDeclaration;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
namedExports.push(exportDeclaration);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
exportWithoutClause,
|
||||||
|
namedExports,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -273,6 +346,12 @@ namespace ts.OrganizeImports {
|
||||||
importDeclaration.moduleSpecifier);
|
importDeclaration.moduleSpecifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function sortSpecifiers<T extends ImportOrExportSpecifier>(specifiers: ReadonlyArray<T>) {
|
||||||
|
return stableSort(specifiers, (s1, s2) =>
|
||||||
|
compareIdentifiers(s1.propertyName || s1.name, s2.propertyName || s2.name) ||
|
||||||
|
compareIdentifiers(s1.name, s2.name));
|
||||||
|
}
|
||||||
|
|
||||||
/* internal */ // Exported for testing
|
/* internal */ // Exported for testing
|
||||||
export function compareModuleSpecifiers(m1: Expression, m2: Expression) {
|
export function compareModuleSpecifiers(m1: Expression, m2: Expression) {
|
||||||
const name1 = getExternalModuleName(m1);
|
const name1 = getExternalModuleName(m1);
|
||||||
|
@ -281,4 +360,8 @@ namespace ts.OrganizeImports {
|
||||||
compareBooleans(isExternalModuleNameRelative(name1), isExternalModuleNameRelative(name2)) ||
|
compareBooleans(isExternalModuleNameRelative(name1), isExternalModuleNameRelative(name2)) ||
|
||||||
compareStringsCaseInsensitive(name1, name2);
|
compareStringsCaseInsensitive(name1, name2);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
function compareIdentifiers(s1: Identifier, s2: Identifier) {
|
||||||
|
return compareStringsCaseInsensitive(s1.text, s2.text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
// ==ORIGINAL==
|
||||||
|
|
||||||
|
declare module "mod" {
|
||||||
|
export { F1 } from "lib";
|
||||||
|
export * from "lib";
|
||||||
|
export { F2 } from "lib";
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==ORGANIZED==
|
||||||
|
|
||||||
|
declare module "mod" {
|
||||||
|
export * from "lib";
|
||||||
|
export { F1, F2 } from "lib";
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
// ==ORIGINAL==
|
||||||
|
|
||||||
|
export { d } from "lib1";
|
||||||
|
export { b } from "lib1";
|
||||||
|
export { c } from "lib2";
|
||||||
|
export { a } from "lib2";
|
||||||
|
|
||||||
|
// ==ORGANIZED==
|
||||||
|
|
||||||
|
export { b, d } from "lib1";
|
||||||
|
export { a, c } from "lib2";
|
|
@ -0,0 +1,8 @@
|
||||||
|
// ==ORIGINAL==
|
||||||
|
|
||||||
|
/*A*/export /*B*/ { /*C*/ F2 /*D*/ } /*E*/ from /*F*/ "lib" /*G*/;/*H*/ //I
|
||||||
|
/*J*/export /*K*/ { /*L*/ F1 /*M*/ } /*N*/ from /*O*/ "lib" /*P*/;/*Q*/ //R
|
||||||
|
|
||||||
|
// ==ORGANIZED==
|
||||||
|
|
||||||
|
/*A*/export /*B*/ { /*L*/ F1 /*M*/, /*C*/ F2 /*D*/ } /*E*/ from /*F*/ "lib" /*G*/; /*H*/ //I
|
|
@ -0,0 +1,13 @@
|
||||||
|
// ==ORIGINAL==
|
||||||
|
|
||||||
|
export { F1, F2 } from "lib";
|
||||||
|
1;
|
||||||
|
export * from "lib";
|
||||||
|
2;
|
||||||
|
|
||||||
|
// ==ORGANIZED==
|
||||||
|
|
||||||
|
export * from "lib";
|
||||||
|
export { F1, F2 } from "lib";
|
||||||
|
1;
|
||||||
|
2;
|
|
@ -0,0 +1,20 @@
|
||||||
|
// ==ORIGINAL==
|
||||||
|
|
||||||
|
export { F1, F2 } from "lib";
|
||||||
|
1;
|
||||||
|
export * from "lib";
|
||||||
|
2;
|
||||||
|
export { b } from `${'lib'}`;
|
||||||
|
export { a } from `${'lib'}`;
|
||||||
|
export { D } from "lib";
|
||||||
|
3;
|
||||||
|
|
||||||
|
// ==ORGANIZED==
|
||||||
|
|
||||||
|
export * from "lib";
|
||||||
|
export { D, F1, F2 } from "lib";
|
||||||
|
export { b } from `${'lib'}`;
|
||||||
|
export { a } from `${'lib'}`;
|
||||||
|
1;
|
||||||
|
2;
|
||||||
|
3;
|
|
@ -0,0 +1,23 @@
|
||||||
|
// ==ORIGINAL==
|
||||||
|
|
||||||
|
export { F1, F2 } from "lib";
|
||||||
|
1;
|
||||||
|
import { F1, F2 } from "lib";
|
||||||
|
2;
|
||||||
|
export * from "lib";
|
||||||
|
3;
|
||||||
|
import * as NS from "lib";
|
||||||
|
4;
|
||||||
|
F1(); F2(); NS.F1();
|
||||||
|
|
||||||
|
// ==ORGANIZED==
|
||||||
|
|
||||||
|
export * from "lib";
|
||||||
|
export { F1, F2 } from "lib";
|
||||||
|
1;
|
||||||
|
import * as NS from "lib";
|
||||||
|
import { F1, F2 } from "lib";
|
||||||
|
2;
|
||||||
|
3;
|
||||||
|
4;
|
||||||
|
F1(); F2(); NS.F1();
|
|
@ -0,0 +1,23 @@
|
||||||
|
// ==ORIGINAL==
|
||||||
|
|
||||||
|
import { F1, F2 } from "lib";
|
||||||
|
1;
|
||||||
|
export { F1, F2 } from "lib";
|
||||||
|
2;
|
||||||
|
import * as NS from "lib";
|
||||||
|
3;
|
||||||
|
export * from "lib";
|
||||||
|
4;
|
||||||
|
F1(); F2(); NS.F1();
|
||||||
|
|
||||||
|
// ==ORGANIZED==
|
||||||
|
|
||||||
|
import * as NS from "lib";
|
||||||
|
import { F1, F2 } from "lib";
|
||||||
|
1;
|
||||||
|
export * from "lib";
|
||||||
|
export { F1, F2 } from "lib";
|
||||||
|
2;
|
||||||
|
3;
|
||||||
|
4;
|
||||||
|
F1(); F2(); NS.F1();
|
|
@ -0,0 +1,11 @@
|
||||||
|
// ==ORIGINAL==
|
||||||
|
|
||||||
|
// Header
|
||||||
|
export * from "lib2";
|
||||||
|
export * from "lib1";
|
||||||
|
|
||||||
|
// ==ORGANIZED==
|
||||||
|
|
||||||
|
// Header
|
||||||
|
export * from "lib1";
|
||||||
|
export * from "lib2";
|
|
@ -0,0 +1,9 @@
|
||||||
|
// ==ORIGINAL==
|
||||||
|
|
||||||
|
/*A*/export /*B*/ * /*C*/ from /*D*/ "lib2" /*E*/;/*F*/ //G
|
||||||
|
/*H*/export /*I*/ * /*J*/ from /*K*/ "lib1" /*L*/;/*M*/ //N
|
||||||
|
|
||||||
|
// ==ORGANIZED==
|
||||||
|
|
||||||
|
/*A*//*H*/ export /*I*/ * /*J*/ from /*K*/ "lib1" /*L*/; /*M*/ //N
|
||||||
|
export /*B*/ * /*C*/ from /*D*/ "lib2" /*E*/; /*F*/ //G
|
|
@ -0,0 +1,23 @@
|
||||||
|
// ==ORIGINAL==
|
||||||
|
|
||||||
|
export { D } from "lib";
|
||||||
|
|
||||||
|
declare module "mod" {
|
||||||
|
export { F1 } from "lib";
|
||||||
|
export * from "lib";
|
||||||
|
export { F2 } from "lib";
|
||||||
|
}
|
||||||
|
|
||||||
|
export { E } from "lib";
|
||||||
|
export * from "lib";
|
||||||
|
|
||||||
|
// ==ORGANIZED==
|
||||||
|
|
||||||
|
export * from "lib";
|
||||||
|
export { D, E } from "lib";
|
||||||
|
|
||||||
|
declare module "mod" {
|
||||||
|
export * from "lib";
|
||||||
|
export { F1, F2 } from "lib";
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue