mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
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.
This commit is contained in:
committed by
Pawel Kozlowski
parent
7ba8929504
commit
8fa6617352
@@ -278,9 +278,26 @@ function migrateRoute(
|
||||
return routeMigrationResults;
|
||||
}
|
||||
|
||||
const componentImport = route.routeFileImports.find((importDecl) =>
|
||||
importDecl.importClause?.getText().includes(componentClassName),
|
||||
)!;
|
||||
// Resolve the import that provides this component by exact specifier match
|
||||
// Handles default imports, named imports, and aliases (e.g., `import { Foo as Bar }`).
|
||||
const componentImport = route.routeFileImports.find((importDecl) => {
|
||||
const clause = importDecl.importClause;
|
||||
if (!clause) return false;
|
||||
// Default import: import FooComponent from '...'
|
||||
if (clause.name && ts.isIdentifier(clause.name) && clause.name.text === componentClassName) {
|
||||
return true;
|
||||
}
|
||||
// Named imports: import { FooComponent } from '...'
|
||||
const named = clause.namedBindings;
|
||||
if (named && ts.isNamedImports(named)) {
|
||||
return named.elements.some((el: ts.ImportSpecifier) => {
|
||||
// Support alias: import { Foo as Bar }
|
||||
const importedName = el.propertyName ? el.propertyName.text : el.name.text;
|
||||
return importedName === componentClassName;
|
||||
});
|
||||
}
|
||||
return false;
|
||||
})!;
|
||||
|
||||
// remove single and double quotes from the import path
|
||||
let componentImportPath = ts.isStringLiteral(componentImport?.moduleSpecifier)
|
||||
|
||||
@@ -850,6 +850,59 @@ describe('route lazy loading migration', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('should resolve the correct import when one component name is a suffix of another', async () => {
|
||||
writeFile(
|
||||
'app.module.ts',
|
||||
`
|
||||
import {NgModule} from '@angular/core';
|
||||
import {RouterModule} from '@angular/router';
|
||||
import {FooBarComponent} from './foo-bar';
|
||||
import {BarComponent} from './bar';
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forRoot([
|
||||
{path: 'foo-bar', component: FooBarComponent},
|
||||
{path: 'bar', component: BarComponent},
|
||||
])],
|
||||
})
|
||||
export class AppModule {}
|
||||
`,
|
||||
);
|
||||
|
||||
writeFile(
|
||||
'foo-bar.ts',
|
||||
`
|
||||
import {Component} from '@angular/core';
|
||||
@Component({template: 'foo bar', standalone: true})
|
||||
export class FooBarComponent {}
|
||||
`,
|
||||
);
|
||||
|
||||
writeFile(
|
||||
'bar.ts',
|
||||
`
|
||||
import {Component} from '@angular/core';
|
||||
@Component({template: 'bar', standalone: true})
|
||||
export class BarComponent {}
|
||||
`,
|
||||
);
|
||||
|
||||
await runMigration('route-lazy-loading');
|
||||
|
||||
const result = stripWhitespace(tree.readContent('app.module.ts'));
|
||||
|
||||
expect(result).toContain(
|
||||
stripWhitespace(
|
||||
`{path: 'foo-bar', loadComponent: () => import('./foo-bar').then(m => m.FooBarComponent)}`,
|
||||
),
|
||||
);
|
||||
expect(result).toContain(
|
||||
stripWhitespace(
|
||||
`{path: 'bar', loadComponent: () => import('./bar').then(m => m.BarComponent)}`,
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
// TODO: support multiple imports of components
|
||||
// ex import * as Components from './components';
|
||||
// export const MenuRoutes: Routes = [
|
||||
|
||||
Reference in New Issue
Block a user