mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-30 11:24:49 -05:00
fix(54310): "Move to file" does not eliminate re-export (#54329)
This commit is contained in:
@@ -1,8 +1,10 @@
|
||||
import { getModuleSpecifier } from "../../compiler/moduleSpecifiers";
|
||||
import {
|
||||
__String,
|
||||
AnyImportOrRequireStatement,
|
||||
append,
|
||||
ApplicableRefactorInfo,
|
||||
arrayFrom,
|
||||
AssignmentDeclarationKind,
|
||||
BinaryExpression,
|
||||
BindingElement,
|
||||
@@ -26,15 +28,18 @@ import {
|
||||
emptyArray,
|
||||
EnumDeclaration,
|
||||
escapeLeadingUnderscores,
|
||||
ExportDeclaration,
|
||||
Expression,
|
||||
ExpressionStatement,
|
||||
extensionFromPath,
|
||||
ExternalModuleReference,
|
||||
factory,
|
||||
fileShouldUseJavaScriptRequire,
|
||||
filter,
|
||||
find,
|
||||
FindAllReferences,
|
||||
findIndex,
|
||||
findLast,
|
||||
findLastIndex,
|
||||
firstDefined,
|
||||
flatMap,
|
||||
@@ -69,6 +74,8 @@ import {
|
||||
isBinaryExpression,
|
||||
isBindingElement,
|
||||
isDeclarationName,
|
||||
isExportDeclaration,
|
||||
isExportSpecifier,
|
||||
isExpressionStatement,
|
||||
isExternalModuleReference,
|
||||
isFunctionLikeDeclaration,
|
||||
@@ -76,6 +83,7 @@ import {
|
||||
isImportDeclaration,
|
||||
isImportEqualsDeclaration,
|
||||
isNamedDeclaration,
|
||||
isNamedExports,
|
||||
isObjectLiteralExpression,
|
||||
isOmittedExpression,
|
||||
isPrologueDirective,
|
||||
@@ -236,7 +244,7 @@ function getNewStatementsAndRemoveFromOldFile(
|
||||
const body = addExports(oldFile, toMove.all, usage.oldFileImportsFromTargetFile, useEsModuleSyntax);
|
||||
if (typeof targetFile !== "string") {
|
||||
if (targetFile.statements.length > 0) {
|
||||
changes.insertNodesAfter(targetFile, targetFile.statements[targetFile.statements.length - 1], body);
|
||||
moveStatementsToTargetFile(changes, program, body, targetFile, toMove);
|
||||
}
|
||||
else {
|
||||
changes.insertNodesAtEndOfFile(targetFile, body, /*blankLineBetween*/ false);
|
||||
@@ -1137,6 +1145,59 @@ function isNonVariableTopLevelDeclaration(node: Node): node is NonVariableTopLev
|
||||
}
|
||||
}
|
||||
|
||||
function moveStatementsToTargetFile(changes: textChanges.ChangeTracker, program: Program, statements: readonly Statement[], targetFile: SourceFile, toMove: ToMove) {
|
||||
const removedExports = new Set<ExportDeclaration>();
|
||||
const targetExports = targetFile.symbol?.exports;
|
||||
if (targetExports) {
|
||||
const checker = program.getTypeChecker();
|
||||
const targetToSourceExports = new Map<ExportDeclaration, Set<TopLevelDeclaration>>();
|
||||
|
||||
for (const node of toMove.all) {
|
||||
if (isTopLevelDeclarationStatement(node) && hasSyntacticModifier(node, ModifierFlags.Export)) {
|
||||
forEachTopLevelDeclaration(node, declaration => {
|
||||
const targetDeclarations = canHaveSymbol(declaration) ? targetExports.get(declaration.symbol.escapedName)?.declarations : undefined;
|
||||
const exportDeclaration = firstDefined(targetDeclarations, d =>
|
||||
isExportDeclaration(d) ? d :
|
||||
isExportSpecifier(d) ? tryCast(d.parent.parent, isExportDeclaration) : undefined);
|
||||
if (exportDeclaration && exportDeclaration.moduleSpecifier) {
|
||||
targetToSourceExports.set(exportDeclaration,
|
||||
(targetToSourceExports.get(exportDeclaration) || new Set()).add(declaration));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
for (const [exportDeclaration, topLevelDeclarations] of arrayFrom(targetToSourceExports)) {
|
||||
if (exportDeclaration.exportClause && isNamedExports(exportDeclaration.exportClause) && length(exportDeclaration.exportClause.elements)) {
|
||||
const elements = exportDeclaration.exportClause.elements;
|
||||
const updatedElements = filter(elements, elem =>
|
||||
find(skipAlias(elem.symbol, checker).declarations, d => isTopLevelDeclaration(d) && topLevelDeclarations.has(d)) === undefined);
|
||||
|
||||
if (length(updatedElements) === 0) {
|
||||
changes.deleteNode(targetFile, exportDeclaration);
|
||||
removedExports.add(exportDeclaration);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (length(updatedElements) < length(elements)) {
|
||||
changes.replaceNode(targetFile, exportDeclaration,
|
||||
factory.updateExportDeclaration(exportDeclaration, exportDeclaration.modifiers, exportDeclaration.isTypeOnly,
|
||||
factory.updateNamedExports(exportDeclaration.exportClause , factory.createNodeArray(updatedElements, elements.hasTrailingComma)), exportDeclaration.moduleSpecifier, exportDeclaration.assertClause));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const lastReExport = findLast(targetFile.statements, n =>
|
||||
isExportDeclaration(n) && !!n.moduleSpecifier && !removedExports.has(n));
|
||||
if (lastReExport) {
|
||||
changes.insertNodesBefore(targetFile, lastReExport, statements, /*blankLineBetween*/ true);
|
||||
}
|
||||
else {
|
||||
changes.insertNodesAfter(targetFile, targetFile.statements[targetFile.statements.length - 1], statements);
|
||||
}
|
||||
}
|
||||
|
||||
function getOverloadRangeToMove(sourceFile: SourceFile, statement: Statement) {
|
||||
if (isFunctionLikeDeclaration(statement)) {
|
||||
const declarations = statement.symbol.declarations;
|
||||
|
||||
@@ -671,6 +671,10 @@ export class ChangeTracker {
|
||||
this.insertNodeAt(sourceFile, getAdjustedStartPosition(sourceFile, before, options), newNode, this.getOptionsForInsertNodeBefore(before, newNode, blankLineBetween));
|
||||
}
|
||||
|
||||
public insertNodesBefore(sourceFile: SourceFile, before: Node, newNodes: readonly Node[], blankLineBetween = false, options: ConfigurableStartEnd = {}): void {
|
||||
this.insertNodesAt(sourceFile, getAdjustedStartPosition(sourceFile, before, options), newNodes, this.getOptionsForInsertNodeBefore(before, first(newNodes), blankLineBetween));
|
||||
}
|
||||
|
||||
public insertModifierAt(sourceFile: SourceFile, pos: number, modifier: SyntaxKind, options: InsertNodeOptions = {}): void {
|
||||
this.insertNodeAt(sourceFile, pos, factory.createToken(modifier), options);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user