diff --git a/packages/core/schematics/ng-generate/route-lazy-loading/to-lazy-routes.ts b/packages/core/schematics/ng-generate/route-lazy-loading/to-lazy-routes.ts index 7f8c30fd0c0..af9d305d13d 100644 --- a/packages/core/schematics/ng-generate/route-lazy-loading/to-lazy-routes.ts +++ b/packages/core/schematics/ng-generate/route-lazy-loading/to-lazy-routes.ts @@ -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, [ diff --git a/packages/core/schematics/test/standalone_routes_spec.ts b/packages/core/schematics/test/standalone_routes_spec.ts index a4a85fcbcc6..a1f03c8d58c 100644 --- a/packages/core/schematics/test/standalone_routes_spec.ts +++ b/packages/core/schematics/test/standalone_routes_spec.ts @@ -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',