Merge pull request #22087 from amcasey/AmbientModuleImports

Organize imports in ambient modules
This commit is contained in:
Andrew Casey 2018-02-23 17:43:28 -08:00 committed by GitHub
commit b31aa4e012
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 129 additions and 36 deletions

View File

@ -425,8 +425,8 @@ namespace ts {
}
export function isAmbientModule(node: Node): boolean {
return node && node.kind === SyntaxKind.ModuleDeclaration &&
((<ModuleDeclaration>node).name.kind === SyntaxKind.StringLiteral || isGlobalScopeAugmentation(<ModuleDeclaration>node));
return node && isModuleDeclaration(node) &&
(node.name.kind === SyntaxKind.StringLiteral || isGlobalScopeAugmentation(node));
}
export function isModuleWithStringLiteralName(node: Node): node is ModuleDeclaration {

View File

@ -325,6 +325,43 @@ F2();
/*A*/import /*B*/ { /*C*/ F1 /*D*/, /*E*/ F2 /*F*/ } /*G*/ from /*H*/ "lib" /*I*/;/*J*/ //K
F1();
`,
},
libFile);
testOrganizeImports("AmbientModule",
{
path: "/test.ts",
content: `
declare module "mod" {
import { F1 } from "lib";
import * as NS from "lib";
import { F2 } from "lib";
function F(f1: {} = F1, f2: {} = F2) {}
}
`,
},
libFile);
testOrganizeImports("TopLevelAndAmbientModule",
{
path: "/test.ts",
content: `
import D from "lib";
declare module "mod" {
import { F1 } from "lib";
import * as NS from "lib";
import { F2 } from "lib";
function F(f1: {} = F1, f2: {} = F2) {}
}
import E from "lib";
import "lib";
D();
`,
},
libFile);

View File

@ -13,46 +13,55 @@ namespace ts.OrganizeImports {
host: LanguageServiceHost,
program: Program) {
// TODO (https://github.com/Microsoft/TypeScript/issues/10020): sort *within* ambient modules (find using isAmbientModule)
// All of the old ImportDeclarations in the file, in syntactic order.
const oldImportDecls = sourceFile.statements.filter(isImportDeclaration);
if (oldImportDecls.length === 0) {
return [];
}
const oldImportGroups = group(oldImportDecls, importDecl => getExternalModuleName(importDecl.moduleSpecifier));
const sortedImportGroups = stableSort(oldImportGroups, (group1, group2) =>
compareModuleSpecifiers(group1[0].moduleSpecifier, group2[0].moduleSpecifier));
const newImportDecls = flatMap(sortedImportGroups, importGroup =>
getExternalModuleName(importGroup[0].moduleSpecifier)
? coalesceImports(removeUnusedImports(importGroup, sourceFile, program))
: importGroup);
const changeTracker = textChanges.ChangeTracker.fromContext({ host, formatContext });
// Delete or replace the first import.
if (newImportDecls.length === 0) {
changeTracker.deleteNode(sourceFile, oldImportDecls[0]);
}
else {
// Note: Delete the surrounding trivia because it will have been retained in newImportDecls.
changeTracker.replaceNodeWithNodes(sourceFile, oldImportDecls[0], newImportDecls, {
useNonAdjustedStartPosition: false,
useNonAdjustedEndPosition: false,
suffix: getNewLineOrDefaultFromHost(host, formatContext.options),
});
}
// All of the old ImportDeclarations in the file, in syntactic order.
const topLevelImportDecls = sourceFile.statements.filter(isImportDeclaration);
organizeImportsWorker(topLevelImportDecls);
// Delete any subsequent imports.
for (let i = 1; i < oldImportDecls.length; i++) {
changeTracker.deleteNode(sourceFile, oldImportDecls[i]);
for (const ambientModule of sourceFile.statements.filter(isAmbientModule)) {
const ambientModuleBody = getModuleBlock(ambientModule as ModuleDeclaration);
const ambientModuleImportDecls = ambientModuleBody.statements.filter(isImportDeclaration);
organizeImportsWorker(ambientModuleImportDecls);
}
return changeTracker.getChanges();
function organizeImportsWorker(oldImportDecls: ReadonlyArray<ImportDeclaration>) {
if (length(oldImportDecls) === 0) {
return;
}
const oldImportGroups = group(oldImportDecls, importDecl => getExternalModuleName(importDecl.moduleSpecifier));
const sortedImportGroups = stableSort(oldImportGroups, (group1, group2) => compareModuleSpecifiers(group1[0].moduleSpecifier, group2[0].moduleSpecifier));
const newImportDecls = flatMap(sortedImportGroups, importGroup =>
getExternalModuleName(importGroup[0].moduleSpecifier)
? coalesceImports(removeUnusedImports(importGroup, sourceFile, program))
: importGroup);
// Delete or replace the first import.
if (newImportDecls.length === 0) {
changeTracker.deleteNode(sourceFile, oldImportDecls[0]);
}
else {
// Note: Delete the surrounding trivia because it will have been retained in newImportDecls.
changeTracker.replaceNodeWithNodes(sourceFile, oldImportDecls[0], newImportDecls, {
useNonAdjustedStartPosition: false,
useNonAdjustedEndPosition: false,
suffix: getNewLineOrDefaultFromHost(host, formatContext.options),
});
}
// Delete any subsequent imports.
for (let i = 1; i < oldImportDecls.length; i++) {
changeTracker.deleteNode(sourceFile, oldImportDecls[i]);
}
}
}
function getModuleBlock(moduleDecl: ModuleDeclaration): ModuleBlock | undefined {
const body = moduleDecl.body;
return body && !isIdentifier(body) && (isModuleBlock(body) ? body : getModuleBlock(body));
}
function removeUnusedImports(oldImports: ReadonlyArray<ImportDeclaration>, sourceFile: SourceFile, program: Program) {

View File

@ -0,0 +1,17 @@
// ==ORIGINAL==
declare module "mod" {
import { F1 } from "lib";
import * as NS from "lib";
import { F2 } from "lib";
function F(f1: {} = F1, f2: {} = F2) {}
}
// ==ORGANIZED==
declare module "mod" {
import { F1, F2 } from "lib";
function F(f1: {} = F1, f2: {} = F2) {}
}

View File

@ -0,0 +1,30 @@
// ==ORIGINAL==
import D from "lib";
declare module "mod" {
import { F1 } from "lib";
import * as NS from "lib";
import { F2 } from "lib";
function F(f1: {} = F1, f2: {} = F2) {}
}
import E from "lib";
import "lib";
D();
// ==ORGANIZED==
import "lib";
import D from "lib";
declare module "mod" {
import { F1, F2 } from "lib";
function F(f1: {} = F1, f2: {} = F2) {}
}
D();