@@ -19,7 +19,6 @@ interface RemovalLocations {
1919 arrays : UniqueItemTracker < ts . ArrayLiteralExpression , ts . Node > ;
2020 imports : UniqueItemTracker < ts . NamedImports , ts . Node > ;
2121 exports : UniqueItemTracker < ts . NamedExports , ts . Node > ;
22- classes : Set < ts . ClassDeclaration > ;
2322 unknown : Set < ts . Node > ;
2423}
2524
@@ -29,21 +28,33 @@ export function pruneNgModules(
2928 referenceLookupExcludedFiles ?: RegExp ) {
3029 const filesToRemove = new Set < ts . SourceFile > ( ) ;
3130 const tracker = new ChangeTracker ( printer , importRemapper ) ;
32- const typeChecker = program . getTsProgram ( ) . getTypeChecker ( ) ;
31+ const tsProgram = program . getTsProgram ( ) ;
32+ const typeChecker = tsProgram . getTypeChecker ( ) ;
3333 const referenceResolver =
3434 new ReferenceResolver ( program , host , rootFileNames , basePath , referenceLookupExcludedFiles ) ;
3535 const removalLocations : RemovalLocations = {
3636 arrays : new UniqueItemTracker < ts . ArrayLiteralExpression , ts . Node > ( ) ,
3737 imports : new UniqueItemTracker < ts . NamedImports , ts . Node > ( ) ,
3838 exports : new UniqueItemTracker < ts . NamedExports , ts . Node > ( ) ,
39- classes : new Set < ts . ClassDeclaration > ( ) ,
4039 unknown : new Set < ts . Node > ( )
4140 } ;
41+ const classesToRemove = new Set < ts . ClassDeclaration > ( ) ;
42+ const barrelExports = new UniqueItemTracker < ts . SourceFile , ts . ExportDeclaration > ( ) ;
43+ const nodesToRemove = new Set < ts . Node > ( ) ;
4244
4345 sourceFiles . forEach ( function walk ( node : ts . Node ) {
4446 if ( ts . isClassDeclaration ( node ) && canRemoveClass ( node , typeChecker ) ) {
4547 collectRemovalLocations ( node , removalLocations , referenceResolver , program ) ;
46- removalLocations . classes . add ( node ) ;
48+ classesToRemove . add ( node ) ;
49+ } else if (
50+ ts . isExportDeclaration ( node ) && ! node . exportClause && node . moduleSpecifier &&
51+ ts . isStringLiteralLike ( node . moduleSpecifier ) && node . moduleSpecifier . text . startsWith ( '.' ) ) {
52+ const exportedSourceFile =
53+ typeChecker . getSymbolAtLocation ( node . moduleSpecifier ) ?. valueDeclaration ?. getSourceFile ( ) ;
54+
55+ if ( exportedSourceFile ) {
56+ barrelExports . track ( exportedSourceFile , node ) ;
57+ }
4758 }
4859 node . forEachChild ( walk ) ;
4960 } ) ;
@@ -55,10 +66,27 @@ export function pruneNgModules(
5566 removeExportReferences ( removalLocations . exports , tracker ) ;
5667 addRemovalTodos ( removalLocations . unknown , tracker ) ;
5768
58- for ( const node of removalLocations . classes ) {
69+ // Collect all the nodes to be removed before determining which files to delete since we need
70+ // to know it ahead of time when deleting barrel files that export other barrel files.
71+ ( function trackNodesToRemove ( nodes : Set < ts . Node > ) {
72+ for ( const node of nodes ) {
73+ const sourceFile = node . getSourceFile ( ) ;
74+
75+ if ( ! filesToRemove . has ( sourceFile ) && canRemoveFile ( sourceFile , nodes ) ) {
76+ const barrelExportsForFile = barrelExports . get ( sourceFile ) ;
77+ nodesToRemove . add ( node ) ;
78+ filesToRemove . add ( sourceFile ) ;
79+ barrelExportsForFile && trackNodesToRemove ( barrelExportsForFile ) ;
80+ } else {
81+ nodesToRemove . add ( node ) ;
82+ }
83+ }
84+ } ) ( classesToRemove ) ;
85+
86+ for ( const node of nodesToRemove ) {
5987 const sourceFile = node . getSourceFile ( ) ;
6088
61- if ( ! filesToRemove . has ( sourceFile ) && canRemoveFile ( sourceFile , removalLocations . classes ) ) {
89+ if ( ! filesToRemove . has ( sourceFile ) && canRemoveFile ( sourceFile , nodesToRemove ) ) {
6290 filesToRemove . add ( sourceFile ) ;
6391 } else {
6492 tracker . removeNode ( node ) ;
@@ -276,17 +304,17 @@ function isNonEmptyNgModuleProperty(node: ts.Node): node is ts.PropertyAssignmen
276304 * Determines if a file is safe to delete. A file is safe to delete if all it contains are
277305 * import statements, class declarations that are about to be deleted and non-exported code.
278306 * @param sourceFile File that is being checked.
279- * @param classesToBeRemoved Classes that are being removed as a part of the migration.
307+ * @param nodesToBeRemoved Nodes that are being removed as a part of the migration.
280308 */
281- function canRemoveFile ( sourceFile : ts . SourceFile , classesToBeRemoved : Set < ts . ClassDeclaration > ) {
309+ function canRemoveFile ( sourceFile : ts . SourceFile , nodesToBeRemoved : Set < ts . Node > ) {
282310 for ( const node of sourceFile . statements ) {
283- if ( ts . isImportDeclaration ( node ) ||
284- ( ts . isClassDeclaration ( node ) && classesToBeRemoved . has ( node ) ) ) {
311+ if ( ts . isImportDeclaration ( node ) || nodesToBeRemoved . has ( node ) ) {
285312 continue ;
286313 }
287314
288- if ( ts . canHaveModifiers ( node ) &&
289- ts . getModifiers ( node ) ?. some ( m => m . kind === ts . SyntaxKind . ExportKeyword ) ) {
315+ if ( ts . isExportDeclaration ( node ) ||
316+ ( ts . canHaveModifiers ( node ) &&
317+ ts . getModifiers ( node ) ?. some ( m => m . kind === ts . SyntaxKind . ExportKeyword ) ) ) {
290318 return false ;
291319 }
292320 }
0 commit comments