Skip to content

Commit cc284af

Browse files
crisbetodevversion
authored andcommitted
fix(migrations): combine newly-added imports in import manager (angular#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 angular#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)