@@ -113,6 +113,7 @@ export function pruneNgModules(
113113 replaceInComponentImportsArray (
114114 componentImportArrays ,
115115 classesToRemove ,
116+ removalLocations ,
116117 tracker ,
117118 typeChecker ,
118119 templateTypeChecker ,
@@ -257,6 +258,7 @@ function collectChangeLocations(
257258 * Replaces all the leftover modules in component `imports` arrays with their exports.
258259 * @param componentImportArrays All the imports arrays and their nodes that represent NgModules.
259260 * @param classesToRemove Set of classes that were marked for removal.
261+ * @param removalLocations Tracks the different places from which imports should be removed.
260262 * @param tracker
261263 * @param typeChecker
262264 * @param templateTypeChecker
@@ -265,6 +267,7 @@ function collectChangeLocations(
265267function replaceInComponentImportsArray (
266268 componentImportArrays : UniqueItemTracker < ts . ArrayLiteralExpression , ts . Node > ,
267269 classesToRemove : Set < ts . ClassDeclaration > ,
270+ removalLocations : RemovalLocations ,
268271 tracker : ChangeTracker ,
269272 typeChecker : ts . TypeChecker ,
270273 templateTypeChecker : TemplateTypeChecker ,
@@ -281,6 +284,7 @@ function replaceInComponentImportsArray(
281284 const usedImports = new Set (
282285 findTemplateDependencies ( closestClass , templateTypeChecker ) . map ( ( ref ) => ref . node ) ,
283286 ) ;
287+ const nodesToRemove = new Set < ts . Node > ( ) ;
284288
285289 for ( const node of toReplace ) {
286290 const moduleDecl = findClassDeclaration ( node , typeChecker ) ;
@@ -289,11 +293,29 @@ function replaceInComponentImportsArray(
289293 const moduleMeta = templateTypeChecker . getNgModuleMetadata ( moduleDecl ) ;
290294
291295 if ( moduleMeta ) {
296+ let hasUsedExports = false ;
292297 moduleMeta . exports . forEach ( ( exp ) => {
293298 if ( usedImports . has ( exp . node as NamedClassDeclaration ) ) {
294299 replacements . track ( node , exp as Reference < NamedClassDeclaration > ) ;
300+ hasUsedExports = true ;
295301 }
296302 } ) ;
303+
304+ // If none of the module's exports are used, track the node for removal
305+ if ( ! hasUsedExports ) {
306+ nodesToRemove . add ( node ) ;
307+ } else if ( ts . isIdentifier ( node ) ) {
308+ // Track the import statement for removal when replacing with exports
309+ const symbol = typeChecker . getSymbolAtLocation ( node ) ;
310+ const declarations = symbol ?. declarations ;
311+ if ( declarations ) {
312+ for ( const declaration of declarations ) {
313+ if ( ts . isImportSpecifier ( declaration ) ) {
314+ removalLocations . imports . track ( declaration . parent , declaration ) ;
315+ }
316+ }
317+ }
318+ }
297319 } else {
298320 // It's unlikely not to have module metadata at this point, but just in
299321 // case unmark the class for removal to reduce the chance of breakages.
@@ -302,13 +324,21 @@ function replaceInComponentImportsArray(
302324 }
303325 }
304326
305- replaceModulesInImportsArray ( array , replacements , tracker , templateTypeChecker , importRemapper ) ;
327+ replaceModulesInImportsArray (
328+ array ,
329+ replacements ,
330+ nodesToRemove ,
331+ tracker ,
332+ templateTypeChecker ,
333+ importRemapper ,
334+ ) ;
306335 }
307336}
308337
309338/**
310339 * Replaces all the leftover modules in testing `imports` arrays with their exports.
311340 * @param testImportArrays All test `imports` arrays and their nodes that represent modules.
341+ * @param removalLocations Tracks the different places from which imports should be removed.
312342 * @param classesToRemove Classes marked for removal by the migration.
313343 * @param tracker
314344 * @param typeChecker
@@ -326,6 +356,7 @@ function replaceInTestImportsArray(
326356) {
327357 for ( const [ array , toReplace ] of testImportArrays . getEntries ( ) ) {
328358 const replacements = new UniqueItemTracker < ts . Node , Reference < NamedClassDeclaration > > ( ) ;
359+ const nodesToRemove = new Set < ts . Node > ( ) ;
329360
330361 for ( const node of toReplace ) {
331362 const moduleDecl = findClassDeclaration ( node , typeChecker ) ;
@@ -344,6 +375,19 @@ function replaceInTestImportsArray(
344375 exports . forEach ( ( exp ) =>
345376 replacements . track ( node , exp as Reference < NamedClassDeclaration > ) ,
346377 ) ;
378+
379+ // Track the import statement for removal when replacing with exports
380+ if ( ts . isIdentifier ( node ) ) {
381+ const symbol = typeChecker . getSymbolAtLocation ( node ) ;
382+ const declarations = symbol ?. declarations ;
383+ if ( declarations ) {
384+ for ( const declaration of declarations ) {
385+ if ( ts . isImportSpecifier ( declaration ) ) {
386+ removalLocations . imports . track ( declaration . parent , declaration ) ;
387+ }
388+ }
389+ }
390+ }
347391 } else {
348392 removalLocations . arrays . track ( array , node ) ;
349393 }
@@ -355,26 +399,36 @@ function replaceInTestImportsArray(
355399 }
356400 }
357401
358- replaceModulesInImportsArray ( array , replacements , tracker , templateTypeChecker , importRemapper ) ;
402+ replaceModulesInImportsArray (
403+ array ,
404+ replacements ,
405+ nodesToRemove ,
406+ tracker ,
407+ templateTypeChecker ,
408+ importRemapper ,
409+ ) ;
359410 }
360411}
361412
362413/**
363414 * Replaces any leftover modules in an `imports` arrays with a set of specified exports
364415 * @param array Imports array which is being migrated.
365416 * @param replacements Map of NgModule references to their exports.
417+ * @param nodesToRemove Set of nodes that should be removed without replacement (unused modules).
366418 * @param tracker
419+ * @param typeChecker
367420 * @param templateTypeChecker
368421 * @param importRemapper
369422 */
370423function replaceModulesInImportsArray (
371424 array : ts . ArrayLiteralExpression ,
372425 replacements : UniqueItemTracker < ts . Node , Reference < NamedClassDeclaration > > ,
426+ nodesToRemove : Set < ts . Node > ,
373427 tracker : ChangeTracker ,
374428 templateTypeChecker : TemplateTypeChecker ,
375429 importRemapper ?: DeclarationImportsRemapper ,
376430) : void {
377- if ( replacements . isEmpty ( ) ) {
431+ if ( replacements . isEmpty ( ) && nodesToRemove . size === 0 ) {
378432 return ;
379433 }
380434
@@ -388,6 +442,11 @@ function replaceModulesInImportsArray(
388442 }
389443
390444 for ( const element of array . elements ) {
445+ // Check if this element should be removed entirely (unused module)
446+ if ( nodesToRemove . has ( element ) ) {
447+ continue ;
448+ }
449+
391450 const replacementRefs = replacements . get ( element ) ;
392451
393452 if ( ! replacementRefs ) {
0 commit comments