fix(migrations): fix route-lazy-loading migration (#63818)

route-lazy-loading migration now supports routes when exported by default

PR Close #63818
This commit is contained in:
aparziale
2025-09-16 14:00:26 +02:00
committed by Jessica Janiuk
parent 0afd9c5632
commit 6e54bdfdcb
2 changed files with 105 additions and 3 deletions
@@ -117,9 +117,7 @@ function findRoutesArrayToMigrate(sourceFile: ts.SourceFile, typeChecker: ts.Typ
}
}
}
}
if (ts.isVariableDeclaration(node)) {
} else if (ts.isVariableDeclaration(node)) {
if (isAngularRoutesArray(node, typeChecker)) {
const initializer = node.initializer;
if (
@@ -140,6 +138,37 @@ function findRoutesArrayToMigrate(sourceFile: ts.SourceFile, typeChecker: ts.Typ
});
}
}
} else if (ts.isExportAssignment(node)) {
// Handles `export default routes`, `export default [...]` and `export default [...] as Routes`
let expression = node.expression;
if (ts.isAsExpression(expression)) {
expression = expression.expression;
}
if (ts.isArrayLiteralExpression(expression)) {
routesArrays.push({
routeFilePath: sourceFile.fileName,
array: expression,
routeFileImports: sourceFile.statements.filter(ts.isImportDeclaration),
});
} else if (ts.isIdentifier(expression)) {
manageRoutesExportedByDefault(routesArrays, typeChecker, expression, sourceFile);
}
} else if (ts.isExportDeclaration(node)) {
// Handles cases like `export { routes as default }`
if (node.exportClause && ts.isNamedExports(node.exportClause)) {
for (const specifier of node.exportClause.elements) {
if (specifier.name.text === 'default') {
manageRoutesExportedByDefault(
routesArrays,
typeChecker,
specifier.propertyName ?? specifier.name,
sourceFile,
);
}
}
}
}
node.forEachChild(walk);
@@ -325,6 +354,31 @@ function createLoadComponentPropertyAssignment(
);
}
const manageRoutesExportedByDefault = (
routesArrays: RouteData[],
typeChecker: ts.TypeChecker,
expression: ts.Expression,
sourceFile: ts.SourceFile,
) => {
const symbol = typeChecker.getSymbolAtLocation(expression);
if (!symbol?.declarations) {
return;
}
for (const declaration of symbol.declarations) {
if (
ts.isVariableDeclaration(declaration) &&
declaration.initializer &&
ts.isArrayLiteralExpression(declaration.initializer)
) {
routesArrays.push({
routeFilePath: sourceFile.fileName,
array: declaration.initializer,
routeFileImports: sourceFile.statements.filter(ts.isImportDeclaration),
});
}
}
};
// import('./path)
const createImportCallExpression = (componentImportPath: string) =>
ts.factory.createCallExpression(ts.factory.createIdentifier('import'), undefined, [
@@ -804,6 +804,54 @@ describe('route lazy loading migration', () => {
expect(error).toMatch(/Could not find any files to migrate under the path/);
});
it('should migrate if routes are exported as default', async () => {
writeFile(
'app.module.ts',
`
import {NgModule} from '@angular/core';
import {RouterModule} from '@angular/router';
import {routes} from './routes';
@NgModule({
imports: [RouterModule.forRoot(routes],
})
export class AppModule {}
`,
);
writeFile(
'routes.ts',
`
import {Routes, Route} from '@angular/router';
import {TestComponent} from './test';
export default [
{path: 'test', component: TestComponent}
] as Routes;
`,
);
writeFile(
'test.ts',
`
import {Component} from '@angular/core';
@Component({template: 'hello', standalone: true})
export class TestComponent {}
`,
);
await runMigration('route-lazy-loading');
expect(stripWhitespace(tree.readContent('routes.ts'))).toContain(
stripWhitespace(`
import {Routes, Route} from '@angular/router';
export default [
{path: 'test', loadComponent: () => import('./test').then(m => m.TestComponent)}
] as Routes;
`),
);
});
xit('should migrate routes if the routes file in is another file without type', async () => {
writeFile(
'app.module.ts',