Skip to content

Commit cc284af

Browse files
crisbetodevversion
authored andcommitted
fix(migrations): combine newly-added imports in import manager (#48620)
Fixes that imports weren't being combined in the `ImportManager` when multiple new imports are added for the same file. This wasn't a problem for previous schematics that used the manager, but it'll come up in some of the new ones. Also moves the logic for writing new imports into `recordChanges`, instead of `addImportToSourceFile`. PR Close #48620
1 parent 27da733 commit cc284af

File tree

1 file changed

+61
-20
lines changed

1 file changed

+61
-20
lines changed

packages/core/schematics/utils/import_manager.ts

Lines changed: 61 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,13 @@ export class ImportManager {
2929
new Map<ts.ImportDeclaration, {propertyName?: ts.Identifier, importName: ts.Identifier}[]>();
3030
/** Map of source-files and their previously used identifier names. */
3131
private usedIdentifierNames = new Map<ts.SourceFile, string[]>();
32+
/** Map of source files and the new imports that have to be added to them. */
33+
private newImports: Map<ts.SourceFile, {
34+
importStartIndex: number,
35+
defaultImports: Map<string, ts.Identifier>,
36+
namedImports: Map<string, ts.ImportSpecifier[]>,
37+
}> = new Map();
38+
3239
/**
3340
* Array of previously resolved symbol imports. Cache can be re-used to return
3441
* the same identifier without checking the source-file again.
@@ -142,37 +149,34 @@ export class ImportManager {
142149
}
143150

144151
let identifier: ts.Identifier|null = null;
145-
let newImport: ts.ImportDeclaration|null = null;
152+
153+
if (!this.newImports.has(sourceFile)) {
154+
this.newImports.set(sourceFile, {
155+
importStartIndex,
156+
defaultImports: new Map(),
157+
namedImports: new Map(),
158+
});
159+
}
146160

147161
if (symbolName) {
148162
const propertyIdentifier = ts.factory.createIdentifier(symbolName);
149163
const generatedUniqueIdentifier = this._getUniqueIdentifier(sourceFile, symbolName);
150164
const needsGeneratedUniqueName = generatedUniqueIdentifier.text !== symbolName;
165+
const importMap = this.newImports.get(sourceFile)!.namedImports;
151166
identifier = needsGeneratedUniqueName ? generatedUniqueIdentifier : propertyIdentifier;
152167

153-
newImport = createImportDeclaration(
154-
undefined,
155-
ts.factory.createImportClause(
156-
false, undefined,
157-
ts.factory.createNamedImports([ts.factory.createImportSpecifier(
158-
false, needsGeneratedUniqueName ? propertyIdentifier : undefined, identifier)])),
159-
ts.factory.createStringLiteral(moduleName));
168+
if (!importMap.has(moduleName)) {
169+
importMap.set(moduleName, []);
170+
}
171+
172+
importMap.get(moduleName)!.push(ts.factory.createImportSpecifier(
173+
false, needsGeneratedUniqueName ? propertyIdentifier : undefined, identifier));
160174
} else {
175+
const importMap = this.newImports.get(sourceFile)!.defaultImports;
161176
identifier = this._getUniqueIdentifier(sourceFile, 'defaultExport');
162-
newImport = createImportDeclaration(
163-
undefined, ts.factory.createImportClause(false, identifier, undefined),
164-
ts.factory.createStringLiteral(moduleName));
177+
importMap.set(moduleName, identifier);
165178
}
166179

167-
const newImportText = this.printer.printNode(ts.EmitHint.Unspecified, newImport, sourceFile);
168-
// If the import is generated at the start of the source file, we want to add
169-
// a new-line after the import. Otherwise if the import is generated after an
170-
// existing import, we need to prepend a new-line so that the import is not on
171-
// the same line as the existing import anchor.
172-
this.getUpdateRecorder(sourceFile)
173-
.addNewImport(
174-
importStartIndex, importStartIndex === 0 ? `${newImportText}\n` : `\n${newImportText}`);
175-
176180
// Keep track of all generated imports so that we don't generate duplicate
177181
// similar imports as these can't be statically analyzed in the source-file yet.
178182
this.importCache.push({sourceFile, symbolName, moduleName, identifier});
@@ -200,6 +204,30 @@ export class ImportManager {
200204
this.printer.printNode(ts.EmitHint.Unspecified, newNamedBindings, sourceFile);
201205
recorder.updateExistingImport(namedBindings, newNamedBindingsText);
202206
});
207+
208+
this.newImports.forEach(({importStartIndex, defaultImports, namedImports}, sourceFile) => {
209+
const recorder = this.getUpdateRecorder(sourceFile);
210+
211+
defaultImports.forEach((identifier, moduleName) => {
212+
const newImport = createImportDeclaration(
213+
undefined, ts.factory.createImportClause(false, identifier, undefined),
214+
ts.factory.createStringLiteral(moduleName));
215+
216+
recorder.addNewImport(
217+
importStartIndex, this._getNewImportText(importStartIndex, newImport, sourceFile));
218+
});
219+
220+
namedImports.forEach((specifiers, moduleName) => {
221+
const newImport = createImportDeclaration(
222+
undefined,
223+
ts.factory.createImportClause(
224+
false, undefined, ts.factory.createNamedImports(specifiers)),
225+
ts.factory.createStringLiteral(moduleName));
226+
227+
recorder.addNewImport(
228+
importStartIndex, this._getNewImportText(importStartIndex, newImport, sourceFile));
229+
});
230+
});
203231
}
204232

205233
/** Gets an unique identifier with a base name for the given source file. */
@@ -260,6 +288,19 @@ export class ImportManager {
260288
}
261289
return commentRanges[commentRanges.length - 1]!.end;
262290
}
291+
292+
/** Gets the text that should be added to the file for a newly-created import declaration. */
293+
private _getNewImportText(
294+
importStartIndex: number, newImport: ts.ImportDeclaration,
295+
sourceFile: ts.SourceFile): string {
296+
const text = this.printer.printNode(ts.EmitHint.Unspecified, newImport, sourceFile);
297+
298+
// If the import is generated at the start of the source file, we want to add
299+
// a new-line after the import. Otherwise if the import is generated after an
300+
// existing import, we need to prepend a new-line so that the import is not on
301+
// the same line as the existing import anchor
302+
return importStartIndex === 0 ? `${text}\n` : `\n${text}`;
303+
}
263304
}
264305

265306
/**

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy