Skip to content

Sort exports when organizeImports is run#24237

Merged
amcasey merged 4 commits into
microsoft:masterfrom
amcasey:GH23640
May 18, 2018
Merged

Sort exports when organizeImports is run#24237
amcasey merged 4 commits into
microsoft:masterfrom
amcasey:GH23640

Conversation

@amcasey
Copy link
Copy Markdown
Member

@amcasey amcasey commented May 18, 2018

Note that there's no attempt to remove unused exports. I also didn't move the existing import tests into a corresponding describe for consistency because I wanted to avoid blowing up the diff.

Fixes #23640

Note that there's no attempt to remove unused exports.

Fixes microsoft#23640
@amcasey amcasey requested a review from a user May 18, 2018 01:44
@amcasey
Copy link
Copy Markdown
Member Author

amcasey commented May 18, 2018

Just for you, @DanielRosenwasser.

assertListEqual(actualCoalescedExports, expectedCoalescedExports);
});

it("Sort specifiers - case-insensitive", () => {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like the test above this one is unnecessary?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, deleted.


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);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this isn't really a filter, this and parseImports could use a helper function:

function castEvery<T, U extends T>(inputs: ReadonlyArray<T>, pred: (t: T) => t is U): ReadonlyArray<U> {
    for (const input of inputs)
        Debug.assert(pred(input));
    return inputs as ReadonlyArray<U>;
}

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I understand what benefit this provides.

Comment thread src/services/organizeImports.ts Outdated
return changeTracker.getChanges();

function organizeImportsWorker(oldImportDecls: ReadonlyArray<ImportDeclaration>) {
function organizeImportsWorker(oldImportDecls: ReadonlyArray<ImportDeclaration | ExportDeclaration>) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To keep it type-safe this could be written as:

function organizeImportsWorker<T extends ImportDeclaration | ExportDeclaration>(oldImportDecls: ReadonlyArray<T>, coalesce: (t: ReadonlyArray<T>) => ReadonlyArray<T>)

And called with:

const coalesceAndOrganizeImports = (importGroup: ReadonlyArray<ImportDeclaration>) => coalesceImports(removeUnusedImports(importGroup, sourceFile, program));
...
organizeImportsWorker(topLevelImportDecls, coalesceAndOrganizeImports);
...
organizeImportsWorker(topLevelExportDecls, coalesceExports);

Then inside the function you can avoid the casts and just:

const newImportDecls = flatMap(sortedImportGroups, importGroup => getExternalModuleName(importGroup[0].moduleSpecifier) ? coalesce(importGroup) : importGroup);

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Personally, I find that pulling a single line out of a method and moving it offscreen makes the method harder to read, but I agree that runtime type checks are undesirable (even though we're using JS).

Having said that, I found that I was fighting the type system for most of this change. For instance, I would have liked to type the parameter as ReadonlyArray<ImportDeclaration> | ReadonlyArray<ExportDeclaration>, but that caused even more problems.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've implemented it and it looks reasonable.

Comment thread src/services/organizeImports.ts Outdated
const newExportSpecifiers: ExportSpecifier[] = [];
newExportSpecifiers.push(...flatMap(namedExports, i => (i.exportClause).elements));

const sortedExportSpecifiers = stableSort(newExportSpecifiers, (s1, s2) =>
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's duplicate code in coalesceImports, could they share?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made getCategorizedImports shared and I find the result much uglier than what I started with. Did you have something specific in mind?

Copy link
Copy Markdown

@ghost ghost May 18, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, not getCategorizedX, just the stableSort:

function sortSpecifiers<T extends ImportSpecifier | ExportSpecifier>(specifiers: ReadonlyArray<T>): ReadonlyArray<T> {
    return stableSort(specifiers, (s1, s2) =>
        compareIdentifiers(s1.propertyName || s1.name, s2.propertyName || s2.name) ||
        compareIdentifiers(s1.name, s2.name));
}

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@amcasey amcasey merged commit 04a3512 into microsoft:master May 18, 2018
@amcasey amcasey deleted the GH23640 branch May 18, 2018 20:39
@microsoft microsoft locked and limited conversation to collaborators Jul 31, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant