-
Notifications
You must be signed in to change notification settings - Fork 12.9k
Sort exports when organizeImports is run #24237
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Note that there's no attempt to remove unused exports. Fixes microsoft#23640
Just for you, @DanielRosenwasser. |
assertListEqual(actualCoalescedExports, expectedCoalescedExports); | ||
}); | ||
|
||
it("Sort specifiers - case-insensitive", () => { |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, deleted.
@@ -509,6 +736,13 @@ import { React, Other } from "react"; | |||
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); |
There was a problem hiding this comment.
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>;
}
There was a problem hiding this comment.
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.
src/services/organizeImports.ts
Outdated
} | ||
|
||
return changeTracker.getChanges(); | ||
|
||
function organizeImportsWorker(oldImportDecls: ReadonlyArray<ImportDeclaration>) { | ||
function organizeImportsWorker(oldImportDecls: ReadonlyArray<ImportDeclaration | ExportDeclaration>) { |
There was a problem hiding this comment.
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);
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
src/services/organizeImports.ts
Outdated
const newExportSpecifiers: ExportSpecifier[] = []; | ||
newExportSpecifiers.push(...flatMap(namedExports, i => (i.exportClause).elements)); | ||
|
||
const sortedExportSpecifiers = stableSort(newExportSpecifiers, (s1, s2) => |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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));
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
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