Skip to content

Commit 8fa6617

Browse files
tomer953pkozlowski-opensource
authored andcommitted
fix(core): resolve component import by exact specifier in route lazy-loading schematic
Avoid substring matching on importClause.getText() which caused suffix collisions (e.g., BarComponent vs FooBarComponent). Use AST-based matching for default and named (including aliased) imports to reliably resolve the correct import path when generating loadComponent.
1 parent 7ba8929 commit 8fa6617

File tree

2 files changed

+73
-3
lines changed

2 files changed

+73
-3
lines changed

packages/core/schematics/ng-generate/route-lazy-loading/to-lazy-routes.ts

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -278,9 +278,26 @@ function migrateRoute(
278278
return routeMigrationResults;
279279
}
280280

281-
const componentImport = route.routeFileImports.find((importDecl) =>
282-
importDecl.importClause?.getText().includes(componentClassName),
283-
)!;
281+
// Resolve the import that provides this component by exact specifier match
282+
// Handles default imports, named imports, and aliases (e.g., `import { Foo as Bar }`).
283+
const componentImport = route.routeFileImports.find((importDecl) => {
284+
const clause = importDecl.importClause;
285+
if (!clause) return false;
286+
// Default import: import FooComponent from '...'
287+
if (clause.name && ts.isIdentifier(clause.name) && clause.name.text === componentClassName) {
288+
return true;
289+
}
290+
// Named imports: import { FooComponent } from '...'
291+
const named = clause.namedBindings;
292+
if (named && ts.isNamedImports(named)) {
293+
return named.elements.some((el: ts.ImportSpecifier) => {
294+
// Support alias: import { Foo as Bar }
295+
const importedName = el.propertyName ? el.propertyName.text : el.name.text;
296+
return importedName === componentClassName;
297+
});
298+
}
299+
return false;
300+
})!;
284301

285302
// remove single and double quotes from the import path
286303
let componentImportPath = ts.isStringLiteral(componentImport?.moduleSpecifier)

packages/core/schematics/test/standalone_routes_spec.ts

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -850,6 +850,59 @@ describe('route lazy loading migration', () => {
850850
);
851851
});
852852

853+
it('should resolve the correct import when one component name is a suffix of another', async () => {
854+
writeFile(
855+
'app.module.ts',
856+
`
857+
import {NgModule} from '@angular/core';
858+
import {RouterModule} from '@angular/router';
859+
import {FooBarComponent} from './foo-bar';
860+
import {BarComponent} from './bar';
861+
862+
@NgModule({
863+
imports: [RouterModule.forRoot([
864+
{path: 'foo-bar', component: FooBarComponent},
865+
{path: 'bar', component: BarComponent},
866+
])],
867+
})
868+
export class AppModule {}
869+
`,
870+
);
871+
872+
writeFile(
873+
'foo-bar.ts',
874+
`
875+
import {Component} from '@angular/core';
876+
@Component({template: 'foo bar', standalone: true})
877+
export class FooBarComponent {}
878+
`,
879+
);
880+
881+
writeFile(
882+
'bar.ts',
883+
`
884+
import {Component} from '@angular/core';
885+
@Component({template: 'bar', standalone: true})
886+
export class BarComponent {}
887+
`,
888+
);
889+
890+
await runMigration('route-lazy-loading');
891+
892+
const result = stripWhitespace(tree.readContent('app.module.ts'));
893+
894+
expect(result).toContain(
895+
stripWhitespace(
896+
`{path: 'foo-bar', loadComponent: () => import('./foo-bar').then(m => m.FooBarComponent)}`,
897+
),
898+
);
899+
expect(result).toContain(
900+
stripWhitespace(
901+
`{path: 'bar', loadComponent: () => import('./bar').then(m => m.BarComponent)}`,
902+
),
903+
);
904+
});
905+
853906
// TODO: support multiple imports of components
854907
// ex import * as Components from './components';
855908
// export const MenuRoutes: Routes = [

0 commit comments

Comments
 (0)