diff --git a/docs/generated/packages/angular.json b/docs/generated/packages/angular.json index 8fb15aaf62e75..0e7b453f68a0d 100644 --- a/docs/generated/packages/angular.json +++ b/docs/generated/packages/angular.json @@ -892,10 +892,9 @@ "default": false, "description": "Add `RouterModule.forChild` when set to true, and a simple array of routes when set to false." }, - "parentModule": { + "parent": { "type": "string", - "description": "Update the router configuration of the parent module using `loadChildren` or `children`, depending on what `lazy` is set to.", - "alias": "parent" + "description": "Path to the parent route configuration using `loadChildren` or `children`, depending on what `lazy` is set to." }, "tags": { "type": "string", diff --git a/packages/angular/src/generators/application/__snapshots__/application.spec.ts.snap b/packages/angular/src/generators/application/__snapshots__/application.spec.ts.snap index 618a807e24547..2381a84905d41 100644 --- a/packages/angular/src/generators/application/__snapshots__/application.spec.ts.snap +++ b/packages/angular/src/generators/application/__snapshots__/application.spec.ts.snap @@ -30,17 +30,24 @@ import { bootstrapApplication } from '@angular/platform-browser'; import { RouterModule } from '@angular/router'; import { AppComponent } from './app/app.component'; import { environment } from './environments/environment'; +import { appRoutes } from './app/app.routes'; if (environment.production) { enableProdMode(); } bootstrapApplication(AppComponent, { - providers: [importProvidersFrom(RouterModule.forRoot([], {initialNavigation: 'enabledBlocking'}))], + providers: [importProvidersFrom(RouterModule.forRoot(appRoutes, {initialNavigation: 'enabledBlocking'}))], }).catch((err) => console.error(err));" `; exports[`app --standalone should generate a standalone app correctly with routing 2`] = ` +"import { Route } from '@angular/router'; + +export const appRoutes: Route[] = [];" +`; + +exports[`app --standalone should generate a standalone app correctly with routing 3`] = ` "import { NxWelcomeComponent } from './nx-welcome.component'; import { RouterModule } from '@angular/router'; import { Component } from '@angular/core'; @@ -57,7 +64,7 @@ export class AppComponent { }" `; -exports[`app --standalone should generate a standalone app correctly with routing 3`] = ` +exports[`app --standalone should generate a standalone app correctly with routing 4`] = ` "import { TestBed } from '@angular/core/testing'; import { AppComponent } from './app.component'; import { NxWelcomeComponent } from './nx-welcome.component'; @@ -96,6 +103,7 @@ import { bootstrapApplication } from '@angular/platform-browser';; import { AppComponent } from './app/app.component'; import { environment } from './environments/environment'; + if (environment.production) { enableProdMode(); } diff --git a/packages/angular/src/generators/application/application.spec.ts b/packages/angular/src/generators/application/application.spec.ts index 2ce88770764de..6d361840fd83a 100644 --- a/packages/angular/src/generators/application/application.spec.ts +++ b/packages/angular/src/generators/application/application.spec.ts @@ -1089,6 +1089,9 @@ describe('app', () => { expect( appTree.read('apps/standalone/src/main.ts', 'utf-8') ).toMatchSnapshot(); + expect( + appTree.read('apps/standalone/src/app/app.routes.ts', 'utf-8') + ).toMatchSnapshot(); expect( appTree.read('apps/standalone/src/app/app.component.ts', 'utf-8') ).toMatchSnapshot(); diff --git a/packages/angular/src/generators/application/files/src/app/app.routes.ts__tpl__ b/packages/angular/src/generators/application/files/src/app/app.routes.ts__tpl__ new file mode 100644 index 0000000000000..5ef938d9c4b81 --- /dev/null +++ b/packages/angular/src/generators/application/files/src/app/app.routes.ts__tpl__ @@ -0,0 +1,3 @@ +import { Route } from '@angular/router'; + +export const appRoutes: Route[] = []; \ No newline at end of file diff --git a/packages/angular/src/generators/application/lib/convert-to-standalone-app.ts b/packages/angular/src/generators/application/lib/convert-to-standalone-app.ts index 1bcd1691cc54c..0ff0559d8feb2 100644 --- a/packages/angular/src/generators/application/lib/convert-to-standalone-app.ts +++ b/packages/angular/src/generators/application/lib/convert-to-standalone-app.ts @@ -56,6 +56,7 @@ import { RouterModule } from '@angular/router'` }; import { AppComponent } from './app/app.component'; import { environment } from './environments/environment'; +${routerModuleSetup ? `import { appRoutes } from './app/app.routes';` : ''} if (environment.production) { enableProdMode(); diff --git a/packages/angular/src/generators/application/lib/create-files.ts b/packages/angular/src/generators/application/lib/create-files.ts index ed1a081d8c68a..1588633a0c487 100644 --- a/packages/angular/src/generators/application/lib/create-files.ts +++ b/packages/angular/src/generators/application/lib/create-files.ts @@ -3,18 +3,24 @@ import { generateFiles, joinPathFragments } from '@nrwl/devkit'; import { getRelativePathToRootTsConfig } from '@nrwl/workspace/src/utilities/typescript'; import type { NormalizedSchema } from './normalized-schema'; -export function createFiles(host: Tree, options: NormalizedSchema) { +export function createFiles(tree: Tree, options: NormalizedSchema) { generateFiles( - host, + tree, joinPathFragments(__dirname, '../files'), options.appProjectRoot, { ...options, rootTsConfigPath: getRelativePathToRootTsConfig( - host, + tree, options.appProjectRoot ), tpl: '', } ); + + if (!options.routing) { + tree.delete( + joinPathFragments(options.appProjectRoot, 'src/app/app.routes.ts') + ); + } } diff --git a/packages/angular/src/generators/application/lib/root-router-config.ts b/packages/angular/src/generators/application/lib/root-router-config.ts index 0a4188f4395b0..b31f69878d5a1 100644 --- a/packages/angular/src/generators/application/lib/root-router-config.ts +++ b/packages/angular/src/generators/application/lib/root-router-config.ts @@ -26,10 +26,17 @@ export function addRouterRootConfiguration( 'RouterModule', '@angular/router' ); + sourceFile = insertImport( + host, + sourceFile, + modulePath, + 'appRoutes', + './app.routes' + ); sourceFile = addImportToModule( host, sourceFile, modulePath, - `RouterModule.forRoot([], {initialNavigation: 'enabledBlocking'})` + `RouterModule.forRoot(appRoutes, {initialNavigation: 'enabledBlocking'})` ); } diff --git a/packages/angular/src/generators/host/__snapshots__/host.spec.ts.snap b/packages/angular/src/generators/host/__snapshots__/host.spec.ts.snap index 8e5695a3b25f5..988d9a4718948 100644 --- a/packages/angular/src/generators/host/__snapshots__/host.spec.ts.snap +++ b/packages/angular/src/generators/host/__snapshots__/host.spec.ts.snap @@ -19,28 +19,20 @@ module.exports = withModuleFederation(config);" `; exports[`Host App Generator should generate a host with remotes using standalone components 1`] = ` -"import { NxWelcomeComponent } from './app/nx-welcome.component'; - import { enableProdMode, importProvidersFrom } from '@angular/core'; +"import { enableProdMode, importProvidersFrom } from '@angular/core'; import { bootstrapApplication } from '@angular/platform-browser'; import { RouterModule } from '@angular/router'; import { AppComponent } from './app/app.component'; import { environment } from './environments/environment'; +import { appRoutes } from './app/app.routes'; if (environment.production) { enableProdMode(); } bootstrapApplication(AppComponent, { - providers: [importProvidersFrom(RouterModule.forRoot([ - { - path: '', - component: NxWelcomeComponent - }, - { - path: 'remote1', - loadChildren: () => import('remote1/Routes').then(m => m.RemoteRoutes) - },], {initialNavigation: 'enabledBlocking'}))], -}).catch((err) => console.error(err)" + providers: [importProvidersFrom(RouterModule.forRoot(appRoutes, {initialNavigation: 'enabledBlocking'}))], +}).catch((err) => console.error(err));" `; exports[`Host App Generator should generate a host with remotes using standalone components 2`] = ` diff --git a/packages/angular/src/generators/host/host.ts b/packages/angular/src/generators/host/host.ts index a36a346b950ac..27f4a70f0a5a8 100644 --- a/packages/angular/src/generators/host/host.ts +++ b/packages/angular/src/generators/host/host.ts @@ -12,8 +12,7 @@ import applicationGenerator from '../application/application'; import remoteGenerator from '../remote/remote'; import { normalizeProjectName } from '../utils/project'; import * as ts from 'typescript'; -import { addRoute } from '../../utils/nx-devkit/ast-utils'; -import { addStandaloneRoute } from '../../utils/nx-devkit/standalone-utils'; +import { addRoute } from '../../utils/nx-devkit/route-utils'; import { setupMf } from '../setup-mf/setup-mf'; import { E2eTestRunner } from '../../utils/test-runners'; @@ -106,13 +105,10 @@ ${remoteRoutes} const pathToHostRootRoutingFile = joinPathFragments( sourceRoot, - options.standalone ? 'bootstrap.ts' : 'app/app.module.ts' + 'app/app.routes.ts' ); - const hostRootRoutingFile = tree.read(pathToHostRootRoutingFile, 'utf-8'); - if (!hostRootRoutingFile.includes('RouterModule.forRoot(')) { - return; - } + const hostRootRoutingFile = tree.read(pathToHostRootRoutingFile, 'utf-8'); let sourceFile = ts.createSourceFile( pathToHostRootRoutingFile, @@ -121,32 +117,20 @@ ${remoteRoutes} true ); - if (hostRootRoutingFile.includes('@NgModule')) { - sourceFile = addRoute( - tree, - pathToHostRootRoutingFile, - sourceFile, - `{ - path: '', - component: NxWelcomeComponent - }` - ); - } else { - addStandaloneRoute( - tree, - pathToHostRootRoutingFile, - `{ + addRoute( + tree, + pathToHostRootRoutingFile, + `{ path: '', component: NxWelcomeComponent }` - ); + ); - tree.write( - pathToHostRootRoutingFile, - `import { NxWelcomeComponent } from './app/nx-welcome.component'; + tree.write( + pathToHostRootRoutingFile, + `import { NxWelcomeComponent } from './nx-welcome.component'; ${tree.read(pathToHostRootRoutingFile, 'utf-8')}` - ); - } + ); generateFiles( tree, diff --git a/packages/angular/src/generators/library/__snapshots__/library.spec.ts.snap b/packages/angular/src/generators/library/__snapshots__/library.spec.ts.snap index 98c7c181587fa..087555f205033 100644 --- a/packages/angular/src/generators/library/__snapshots__/library.spec.ts.snap +++ b/packages/angular/src/generators/library/__snapshots__/library.spec.ts.snap @@ -169,14 +169,14 @@ describe('MyLibComponent', () => { exports[`lib --standalone should generate a library with a standalone component as entry point with routing setup 1`] = ` "export * from \\"./lib/my-lib/my-lib.component\\"; - export * from './lib/routes'" + export * from './lib/lib.routes'" `; exports[`lib --standalone should generate a library with a standalone component as entry point with routing setup 2`] = ` "import { Route } from '@angular/router'; import { MyLibComponent } from './my-lib/my-lib.component'; - export const MYLIB_ROUTES: Route[] = [ + export const myLibRoutes: Route[] = [ {path: '', component: MyLibComponent} ];" `; @@ -232,77 +232,43 @@ describe('MyLibComponent', () => { exports[`lib --standalone should generate a library with a standalone component as entry point with routing setup and attach it to parent module as a lazy child 1`] = ` "export * from \\"./lib/my-lib/my-lib.component\\"; - export * from './lib/routes'" + export * from './lib/lib.routes'" `; exports[`lib --standalone should generate a library with a standalone component as entry point with routing setup and attach it to parent module as a lazy child 2`] = ` "import { Route } from '@angular/router'; import { MyLibComponent } from './my-lib/my-lib.component'; - export const MYLIB_ROUTES: Route[] = [ + export const myLibRoutes: Route[] = [ {path: '', component: MyLibComponent} ];" `; exports[`lib --standalone should generate a library with a standalone component as entry point with routing setup and attach it to parent module as a lazy child 3`] = ` -"import { NgModule } from '@angular/core'; -import { BrowserModule } from '@angular/platform-browser'; - -import { AppComponent } from './app.component'; -import { NxWelcomeComponent } from './nx-welcome.component'; -import { RouterModule } from '@angular/router'; +"import { Route } from '@angular/router'; -@NgModule({ - declarations: [ - AppComponent, - NxWelcomeComponent - ], - imports: [ - BrowserModule, - RouterModule.forRoot([{path: 'my-lib', loadChildren: () => import('@proj/my-lib').then(m => m.MYLIB_ROUTES)}], {initialNavigation: 'enabledBlocking'}) - ], - providers: [], - bootstrap: [AppComponent] -}) -export class AppModule { } -" +export const appRoutes: Route[] = [ + {path: 'my-lib', loadChildren: () => import('@proj/my-lib').then(m => m.myLibRoutes)},]" `; exports[`lib --standalone should generate a library with a standalone component as entry point with routing setup and attach it to parent module as direct child 1`] = ` "export * from \\"./lib/my-lib/my-lib.component\\"; - export * from './lib/routes'" + export * from './lib/lib.routes'" `; exports[`lib --standalone should generate a library with a standalone component as entry point with routing setup and attach it to parent module as direct child 2`] = ` "import { Route } from '@angular/router'; import { MyLibComponent } from './my-lib/my-lib.component'; - export const MYLIB_ROUTES: Route[] = [ + export const myLibRoutes: Route[] = [ {path: '', component: MyLibComponent} ];" `; exports[`lib --standalone should generate a library with a standalone component as entry point with routing setup and attach it to parent module as direct child 3`] = ` -"import { NgModule } from '@angular/core'; -import { BrowserModule } from '@angular/platform-browser'; - -import { AppComponent } from './app.component'; -import { NxWelcomeComponent } from './nx-welcome.component'; -import { RouterModule } from '@angular/router'; -import { MYLIB_ROUTES } from '@proj/my-lib'; - -@NgModule({ - declarations: [ - AppComponent, - NxWelcomeComponent - ], - imports: [ - BrowserModule, - RouterModule.forRoot([{ path: 'my-lib', children: MYLIB_ROUTES }], {initialNavigation: 'enabledBlocking'}) - ], - providers: [], - bootstrap: [AppComponent] -}) -export class AppModule { } -" +"import { Route } from '@angular/router'; +import { myLibRoutes } from '@proj/my-lib'; + +export const appRoutes: Route[] = [ + { path: 'my-lib', children: myLibRoutes },]" `; diff --git a/packages/angular/src/generators/library/lib/add-children.ts b/packages/angular/src/generators/library/lib/add-children.ts index 3ccd3f3aa37d1..b3a09bf544329 100644 --- a/packages/angular/src/generators/library/lib/add-children.ts +++ b/packages/angular/src/generators/library/lib/add-children.ts @@ -1,29 +1,26 @@ import { names, Tree } from '@nrwl/devkit'; import { insertImport } from '@nrwl/workspace/src/utilities/ast-utils'; import * as ts from 'typescript'; -import { - addImportToModule, - addRoute, -} from '../../../utils/nx-devkit/ast-utils'; +import { addImportToModule } from '../../../utils/nx-devkit/ast-utils'; import { NormalizedSchema } from './normalized-schema'; -import { addStandaloneRoute } from '../../../utils/nx-devkit/standalone-utils'; +import { addRoute } from '../../../utils/nx-devkit/route-utils'; export function addChildren( tree: Tree, options: NormalizedSchema['libraryOptions'] ) { - if (!tree.exists(options.parentModule)) { - throw new Error(`Cannot find '${options.parentModule}'`); + if (!tree.exists(options.parent)) { + throw new Error(`Cannot find '${options.parent}'`); } - const moduleSource = tree.read(options.parentModule, 'utf-8'); + const routeFileSource = tree.read(options.parent, 'utf-8'); const constName = options.standalone - ? `${names(options.name).className.toUpperCase()}_ROUTES` + ? `${names(options.name).propertyName}Routes` : `${names(options.fileName).propertyName}Routes`; const importPath = options.importPath; let sourceFile = ts.createSourceFile( - options.parentModule, - moduleSource, + options.parent, + routeFileSource, ts.ScriptTarget.Latest, true ); @@ -32,7 +29,7 @@ export function addChildren( sourceFile = addImportToModule( tree, sourceFile, - options.parentModule, + options.parent, options.moduleName ); } @@ -40,7 +37,7 @@ export function addChildren( sourceFile = insertImport( tree, sourceFile, - options.parentModule, + options.parent, options.standalone ? constName : `${options.moduleName}, ${constName}`, importPath ); @@ -49,16 +46,5 @@ export function addChildren( names(options.fileName).fileName }', children: ${constName} }`; - if (moduleSource.includes('@NgModule')) { - sourceFile = addRoute(tree, options.parentModule, sourceFile, route); - } else { - addStandaloneRoute( - tree, - options.parentModule, - route, - false, - constName, - importPath - ); - } + addRoute(tree, options.parent, route, false, constName, importPath); } diff --git a/packages/angular/src/generators/library/lib/add-lazy-loaded-router-configuration.ts b/packages/angular/src/generators/library/lib/add-lazy-loaded-router-configuration.ts index b92b5e3e56d23..193784fe9aa41 100644 --- a/packages/angular/src/generators/library/lib/add-lazy-loaded-router-configuration.ts +++ b/packages/angular/src/generators/library/lib/add-lazy-loaded-router-configuration.ts @@ -1,35 +1,57 @@ -import { Tree } from '@nrwl/devkit'; +import { joinPathFragments, names, Tree } from '@nrwl/devkit'; import { insertImport } from '@nrwl/workspace/src/utilities/ast-utils'; import * as ts from 'typescript'; import { addImportToModule } from '../../../utils/nx-devkit/ast-utils'; import { NormalizedSchema } from './normalized-schema'; +import { dirname } from 'path'; export function addLazyLoadedRouterConfiguration( - host: Tree, + tree: Tree, options: NormalizedSchema['libraryOptions'] ) { - const moduleSource = host.read(options.modulePath)!.toString('utf-8'); + const constName = `${names(options.fileName).propertyName}Routes`; + tree.write( + joinPathFragments(dirname(options.modulePath), 'lib.routes.ts'), + `import { Route } from '@angular/router'; + +export const ${constName}: Route[] = [/* {path: '', pathMatch: 'full', component: InsertYourComponentHere} */];` + ); + + const routeFileSource = tree.read(options.modulePath, 'utf-8'); let sourceFile = ts.createSourceFile( options.modulePath, - moduleSource, + routeFileSource, ts.ScriptTarget.Latest, true ); sourceFile = addImportToModule( - host, + tree, sourceFile, options.modulePath, ` - RouterModule.forChild([ - /* {path: '', pathMatch: 'full', component: InsertYourComponentHere} */ - ]) ` + RouterModule.forChild(${constName}) ` ); sourceFile = insertImport( - host, + tree, sourceFile, options.modulePath, 'RouterModule', '@angular/router' ); + sourceFile = insertImport( + tree, + sourceFile, + options.modulePath, + constName, + './lib.routes' + ); + + const pathToIndex = joinPathFragments(options.projectRoot, 'src/index.ts'); + const indexFileContents = tree.read(pathToIndex, 'utf-8'); + tree.write( + pathToIndex, + `${indexFileContents} + export * from './lib/lib.routes';` + ); } diff --git a/packages/angular/src/generators/library/lib/add-load-children.ts b/packages/angular/src/generators/library/lib/add-load-children.ts index 91e178381cef0..f7d57e5a391ce 100644 --- a/packages/angular/src/generators/library/lib/add-load-children.ts +++ b/packages/angular/src/generators/library/lib/add-load-children.ts @@ -1,20 +1,19 @@ import { names, Tree } from '@nrwl/devkit'; import * as ts from 'typescript'; -import { addRoute } from '../../../utils/nx-devkit/ast-utils'; import { NormalizedSchema } from './normalized-schema'; -import { addStandaloneRoute } from '../../../utils/nx-devkit/standalone-utils'; +import { addRoute } from '../../../utils/nx-devkit/route-utils'; export function addLoadChildren( tree: Tree, options: NormalizedSchema['libraryOptions'] ) { - if (!tree.exists(options.parentModule)) { - throw new Error(`Cannot find '${options.parentModule}'`); + if (!tree.exists(options.parent)) { + throw new Error(`Cannot find '${options.parent}'`); } - const moduleSource = tree.read(options.parentModule)!.toString('utf-8'); + const moduleSource = tree.read(options.parent, 'utf-8'); const sourceFile = ts.createSourceFile( - options.parentModule, + options.parent, moduleSource, ts.ScriptTarget.Latest, true @@ -24,13 +23,9 @@ export function addLoadChildren( names(options.fileName).fileName }', loadChildren: () => import('${options.importPath}').then(m => m.${ options.standalone - ? `${names(options.name).className.toUpperCase()}_ROUTES` + ? `${names(options.name).propertyName}Routes` : options.moduleName })}`; - if (moduleSource.includes('@NgModule')) { - addRoute(tree, options.parentModule, sourceFile, route); - } else { - addStandaloneRoute(tree, options.parentModule, route); - } + addRoute(tree, options.parent, route); } diff --git a/packages/angular/src/generators/library/lib/add-module.ts b/packages/angular/src/generators/library/lib/add-module.ts index f03cd768e6071..6bf85f4ee7abd 100644 --- a/packages/angular/src/generators/library/lib/add-module.ts +++ b/packages/angular/src/generators/library/lib/add-module.ts @@ -12,13 +12,13 @@ export function addModule( if (options.routing && options.lazy) { addLazyLoadedRouterConfiguration(host, options); } - if (options.routing && options.lazy && options.parentModule) { + if (options.routing && options.lazy && options.parent) { addLoadChildren(host, options); } if (options.routing && !options.lazy) { addRouterConfiguration(host, options); } - if (options.routing && !options.lazy && options.parentModule) { + if (options.routing && !options.lazy && options.parent) { addChildren(host, options); } } diff --git a/packages/angular/src/generators/library/lib/add-router-configuration.ts b/packages/angular/src/generators/library/lib/add-router-configuration.ts index 9bce033e2d53b..9078c4a85f58a 100644 --- a/packages/angular/src/generators/library/lib/add-router-configuration.ts +++ b/packages/angular/src/generators/library/lib/add-router-configuration.ts @@ -1,19 +1,17 @@ import type { Tree } from '@nrwl/devkit'; -import { names } from '@nrwl/devkit'; -import { - addGlobal, - insertImport, -} from '@nrwl/workspace/src/utilities/ast-utils'; +import { joinPathFragments, names } from '@nrwl/devkit'; +import { insertImport } from '@nrwl/workspace/src/utilities/ast-utils'; import * as ts from 'typescript'; import { addImportToModule } from '../../../utils/nx-devkit/ast-utils'; import { NormalizedSchema } from './normalized-schema'; +import { dirname } from 'path'; export function addRouterConfiguration( - host: Tree, + tree: Tree, options: NormalizedSchema['libraryOptions'] ) { const constName = `${names(options.fileName).propertyName}Routes`; - const moduleSource = host.read(options.modulePath)!.toString('utf-8'); + const moduleSource = tree.read(options.modulePath, 'utf-8'); let moduleSourceFile = ts.createSourceFile( options.modulePath, moduleSource, @@ -22,22 +20,38 @@ export function addRouterConfiguration( ); moduleSourceFile = addImportToModule( - host, + tree, moduleSourceFile, options.modulePath, `RouterModule` ); moduleSourceFile = insertImport( - host, + tree, moduleSourceFile, options.modulePath, 'RouterModule, Route', '@angular/router' ); - moduleSourceFile = addGlobal( - host, + moduleSourceFile = insertImport( + tree, moduleSourceFile, options.modulePath, - `export const ${constName}: Route[] = [];` + constName, + './lib.routes' + ); + + tree.write( + joinPathFragments(dirname(options.modulePath), 'lib.routes.ts'), + `import { Route } from '@angular/router'; + +export const ${constName}: Route[] = [];` + ); + + const pathToIndex = joinPathFragments(options.projectRoot, 'src/index.ts'); + const indexFileContents = tree.read(pathToIndex, 'utf-8'); + tree.write( + pathToIndex, + `${indexFileContents} + export * from './lib/lib.routes';` ); } diff --git a/packages/angular/src/generators/library/lib/add-standalone-component.ts b/packages/angular/src/generators/library/lib/add-standalone-component.ts index 1a57451a5d983..8f238efd06368 100644 --- a/packages/angular/src/generators/library/lib/add-standalone-component.ts +++ b/packages/angular/src/generators/library/lib/add-standalone-component.ts @@ -21,7 +21,7 @@ export async function addStandaloneComponent( if (libraryOptions.routing) { const pathToRoutes = joinPathFragments( libraryOptions.projectRoot, - 'src/lib/routes.ts' + 'src/lib/lib.routes.ts' ); const routesContents = `import { Route } from '@angular/router'; @@ -32,9 +32,9 @@ export async function addStandaloneComponent( `${libraryOptions.fileName}.component` )}'; - export const ${names( - libraryOptions.name - ).className.toUpperCase()}_ROUTES: Route[] = [ + export const ${ + names(libraryOptions.name).propertyName + }Routes: Route[] = [ {path: '', component: ${libraryOptions.standaloneComponentName}} ];`; tree.write(pathToRoutes, routesContents); @@ -48,10 +48,10 @@ export async function addStandaloneComponent( tree.write( pathToEntryFile, `${entryFileContents} - export * from './lib/routes'` + export * from './lib/lib.routes'` ); - if (libraryOptions.parentModule) { + if (libraryOptions.parent) { if (libraryOptions.lazy) { addLoadChildren(tree, libraryOptions); } else { diff --git a/packages/angular/src/generators/library/lib/normalized-schema.ts b/packages/angular/src/generators/library/lib/normalized-schema.ts index c431f37b85980..4c1637fc6fdac 100644 --- a/packages/angular/src/generators/library/lib/normalized-schema.ts +++ b/packages/angular/src/generators/library/lib/normalized-schema.ts @@ -19,7 +19,7 @@ export interface NormalizedSchema { commonModule?: boolean; routing?: boolean; lazy?: boolean; - parentModule?: string; + parent?: string; tags?: string; strict?: boolean; compilationMode?: 'full' | 'partial'; diff --git a/packages/angular/src/generators/library/library.spec.ts b/packages/angular/src/generators/library/library.spec.ts index b0e060c332798..6066f8ba08fb5 100644 --- a/packages/angular/src/generators/library/library.spec.ts +++ b/packages/angular/src/generators/library/library.spec.ts @@ -839,7 +839,7 @@ describe('lib', () => { directory: 'myDir', routing: true, lazy: true, - parentModule: 'apps/myapp/src/app/app.module.ts', + parent: 'apps/myapp/src/app/app.module.ts', }); const moduleContents = tree @@ -855,7 +855,7 @@ describe('lib', () => { routing: true, lazy: true, simpleModuleName: true, - parentModule: 'apps/myapp/src/app/app.module.ts', + parent: 'apps/myapp/src/app/app.module.ts', }); const moduleContents2 = tree @@ -871,7 +871,7 @@ describe('lib', () => { routing: true, lazy: true, simpleModuleName: true, - parentModule: 'apps/myapp/src/app/app.module.ts', + parent: 'apps/myapp/src/app/app.module.ts', }); const moduleContents3 = tree @@ -955,7 +955,7 @@ describe('lib', () => { directory: 'myDir', routing: true, lazy: true, - parentModule: 'apps/myapp/src/app/app.module.ts', + parent: 'apps/myapp/src/app/app.module.ts', }); // ASSERT @@ -994,9 +994,7 @@ describe('lib', () => { .toString() ).toContain('RouterModule'); expect( - tree - .read('libs/my-dir/my-lib/src/lib/my-dir-my-lib.module.ts') - .toString() + tree.read('libs/my-dir/my-lib/src/lib/lib.routes.ts').toString() ).toContain('const myDirMyLibRoutes: Route[] = '); expect( @@ -1006,7 +1004,7 @@ describe('lib', () => { tree.read('libs/my-dir/my-lib2/src/lib/my-lib2.module.ts').toString() ).toContain('RouterModule'); expect( - tree.read('libs/my-dir/my-lib2/src/lib/my-lib2.module.ts').toString() + tree.read('libs/my-dir/my-lib2/src/lib/lib.routes.ts').toString() ).toContain('const myLib2Routes: Route[] = '); }); @@ -1019,7 +1017,7 @@ describe('lib', () => { name: 'myLib', directory: 'myDir', routing: true, - parentModule: 'apps/myapp/src/app/app.module.ts', + parent: 'apps/myapp/src/app/app.module.ts', }); const moduleContents = tree @@ -1031,7 +1029,7 @@ describe('lib', () => { directory: 'myDir', simpleModuleName: true, routing: true, - parentModule: 'apps/myapp/src/app/app.module.ts', + parent: 'apps/myapp/src/app/app.module.ts', }); const moduleContents2 = tree @@ -1042,7 +1040,7 @@ describe('lib', () => { name: 'myLib3', directory: 'myDir', routing: true, - parentModule: 'apps/myapp/src/app/app.module.ts', + parent: 'apps/myapp/src/app/app.module.ts', simpleModuleName: true, }); @@ -1106,7 +1104,7 @@ describe('lib', () => { name: 'myLib', directory: 'myDir', routing: true, - parentModule: 'apps/myapp/src/app/app.module.ts', + parent: 'apps/myapp/src/app/app.module.ts', }); // ASSERT @@ -1463,7 +1461,7 @@ describe('lib', () => { expect(tree.read('libs/my-lib/src/index.ts', 'utf-8')).toMatchSnapshot(); expect( - tree.read('libs/my-lib/src/lib/routes.ts', 'utf-8') + tree.read('libs/my-lib/src/lib/lib.routes.ts', 'utf-8') ).toMatchSnapshot(); expect( tree.read('libs/my-lib/src/lib/my-lib/my-lib.component.ts', 'utf-8') @@ -1487,16 +1485,16 @@ describe('lib', () => { await runLibraryGeneratorWithOpts({ standalone: true, routing: true, - parentModule: 'apps/app1/src/app/app.module.ts', + parent: 'apps/app1/src/app/app.routes.ts', }); // ASSERT expect(tree.read('libs/my-lib/src/index.ts', 'utf-8')).toMatchSnapshot(); expect( - tree.read('libs/my-lib/src/lib/routes.ts', 'utf-8') + tree.read('libs/my-lib/src/lib/lib.routes.ts', 'utf-8') ).toMatchSnapshot(); expect( - tree.read('apps/app1/src/app/app.module.ts', 'utf-8') + tree.read('apps/app1/src/app/app.routes.ts', 'utf-8') ).toMatchSnapshot(); }); @@ -1512,16 +1510,16 @@ describe('lib', () => { standalone: true, routing: true, lazy: true, - parentModule: 'apps/app1/src/app/app.module.ts', + parent: 'apps/app1/src/app/app.routes.ts', }); // ASSERT expect(tree.read('libs/my-lib/src/index.ts', 'utf-8')).toMatchSnapshot(); expect( - tree.read('libs/my-lib/src/lib/routes.ts', 'utf-8') + tree.read('libs/my-lib/src/lib/lib.routes.ts', 'utf-8') ).toMatchSnapshot(); expect( - tree.read('apps/app1/src/app/app.module.ts', 'utf-8') + tree.read('apps/app1/src/app/app.routes.ts', 'utf-8') ).toMatchSnapshot(); }); @@ -1537,27 +1535,17 @@ describe('lib', () => { await runLibraryGeneratorWithOpts({ standalone: true, routing: true, - parentModule: 'apps/app1/src/main.ts', + parent: 'apps/app1/src/app/app.routes.ts', }); // ASSERT - expect(tree.read('apps/app1/src/main.ts', 'utf-8')) + expect(tree.read('apps/app1/src/app/app.routes.ts', 'utf-8')) .toMatchInlineSnapshot(` - "import { enableProdMode, importProvidersFrom } from '@angular/core'; - import { bootstrapApplication } from '@angular/platform-browser'; - import { RouterModule } from '@angular/router'; - import { AppComponent } from './app/app.component'; - import { environment } from './environments/environment'; - import { MYLIB_ROUTES } from '@proj/my-lib'; - - if (environment.production) { - enableProdMode(); - } + "import { Route } from '@angular/router'; + import { myLibRoutes } from '@proj/my-lib'; - bootstrapApplication(AppComponent, { - providers: [importProvidersFrom(RouterModule.forRoot([ - { path: 'my-lib', children: MYLIB_ROUTES },], {initialNavigation: 'enabledBlocking'}))], - }).catch((err) => console.error(err))" + export const appRoutes: Route[] = [ + { path: 'my-lib', children: myLibRoutes },]" `); }); @@ -1574,26 +1562,16 @@ describe('lib', () => { standalone: true, routing: true, lazy: true, - parentModule: 'apps/app1/src/main.ts', + parent: 'apps/app1/src/app/app.routes.ts', }); // ASSERT - expect(tree.read('apps/app1/src/main.ts', 'utf-8')) + expect(tree.read('apps/app1/src/app/app.routes.ts', 'utf-8')) .toMatchInlineSnapshot(` - "import { enableProdMode, importProvidersFrom } from '@angular/core'; - import { bootstrapApplication } from '@angular/platform-browser'; - import { RouterModule } from '@angular/router'; - import { AppComponent } from './app/app.component'; - import { environment } from './environments/environment'; - - if (environment.production) { - enableProdMode(); - } + "import { Route } from '@angular/router'; - bootstrapApplication(AppComponent, { - providers: [importProvidersFrom(RouterModule.forRoot([ - {path: 'my-lib', loadChildren: () => import('@proj/my-lib').then(m => m.MYLIB_ROUTES)},], {initialNavigation: 'enabledBlocking'}))], - }).catch((err) => console.error(err))" + export const appRoutes: Route[] = [ + {path: 'my-lib', loadChildren: () => import('@proj/my-lib').then(m => m.myLibRoutes)},]" `); }); @@ -1609,18 +1587,18 @@ describe('lib', () => { name: 'second', standalone: true, routing: true, - parentModule: 'libs/my-lib/src/lib/routes.ts', + parent: 'libs/my-lib/src/lib/lib.routes.ts', }); // ASSERT - expect(tree.read('libs/my-lib/src/lib/routes.ts', 'utf-8')) + expect(tree.read('libs/my-lib/src/lib/lib.routes.ts', 'utf-8')) .toMatchInlineSnapshot(` "import { Route } from '@angular/router'; import { MyLibComponent } from './my-lib/my-lib.component'; - import { SECOND_ROUTES } from '@proj/second'; + import { secondRoutes } from '@proj/second'; - export const MYLIB_ROUTES: Route[] = [ - { path: 'second', children: SECOND_ROUTES }, + export const myLibRoutes: Route[] = [ + { path: 'second', children: secondRoutes }, {path: '', component: MyLibComponent} ]" `); @@ -1639,17 +1617,17 @@ describe('lib', () => { standalone: true, routing: true, lazy: true, - parentModule: 'libs/my-lib/src/lib/routes.ts', + parent: 'libs/my-lib/src/lib/lib.routes.ts', }); // ASSERT - expect(tree.read('libs/my-lib/src/lib/routes.ts', 'utf-8')) + expect(tree.read('libs/my-lib/src/lib/lib.routes.ts', 'utf-8')) .toMatchInlineSnapshot(` "import { Route } from '@angular/router'; import { MyLibComponent } from './my-lib/my-lib.component'; - export const MYLIB_ROUTES: Route[] = [ - {path: 'second', loadChildren: () => import('@proj/second').then(m => m.SECOND_ROUTES)}, + export const myLibRoutes: Route[] = [ + {path: 'second', loadChildren: () => import('@proj/second').then(m => m.secondRoutes)}, {path: '', component: MyLibComponent} ]" `); diff --git a/packages/angular/src/generators/library/schema.d.ts b/packages/angular/src/generators/library/schema.d.ts index 3791c31df1daf..59aa96dd24d0b 100644 --- a/packages/angular/src/generators/library/schema.d.ts +++ b/packages/angular/src/generators/library/schema.d.ts @@ -21,7 +21,7 @@ export interface Schema { prefix?: string; routing?: boolean; lazy?: boolean; - parentModule?: string; + parent?: string; tags?: string; strict?: boolean; linter?: AngularLinter; diff --git a/packages/angular/src/generators/library/schema.json b/packages/angular/src/generators/library/schema.json index 942b1445990ae..eb0318baae6a1 100644 --- a/packages/angular/src/generators/library/schema.json +++ b/packages/angular/src/generators/library/schema.json @@ -76,10 +76,9 @@ "default": false, "description": "Add `RouterModule.forChild` when set to true, and a simple array of routes when set to false." }, - "parentModule": { + "parent": { "type": "string", - "description": "Update the router configuration of the parent module using `loadChildren` or `children`, depending on what `lazy` is set to.", - "alias": "parent" + "description": "Path to the parent route configuration using `loadChildren` or `children`, depending on what `lazy` is set to." }, "tags": { "type": "string", diff --git a/packages/angular/src/generators/remote/__snapshots__/remote.spec.ts.snap b/packages/angular/src/generators/remote/__snapshots__/remote.spec.ts.snap index d1324a8c3c36a..02633d0173c21 100644 --- a/packages/angular/src/generators/remote/__snapshots__/remote.spec.ts.snap +++ b/packages/angular/src/generators/remote/__snapshots__/remote.spec.ts.snap @@ -24,7 +24,7 @@ import {enableProdMode, importProvidersFrom} from \\"@angular/core\\"; import {bootstrapApplication} from \\"@angular/platform-browser\\"; import {RouterModule} from \\"@angular/router\\"; import {RemoteEntryComponent} from \\"./app/remote-entry/entry.component\\"; -import {RemoteRoutes} from \\"./app/remote-entry/routes\\"; +import {appRoutes} from \\"./app/app.routes\\"; if (environment.production) { enableProdMode(); @@ -33,7 +33,7 @@ if (environment.production) { bootstrapApplication(RemoteEntryComponent, { providers: [ importProvidersFrom( - RouterModule.forRoot(RemoteRoutes, {initialNavigation: 'enabledBlocking'}) + RouterModule.forRoot(appRoutes, {initialNavigation: 'enabledBlocking'}) ) ] });" @@ -43,7 +43,7 @@ exports[`MF Remote App Generator should generate the a remote setup for standalo "module.exports = { name: 'test', exposes: { - './Routes': 'apps/test/src/app/remote-entry/routes.ts', + './Routes': 'apps/test/src/app/remote-entry/entry.routes.ts', }, }" `; @@ -65,7 +65,14 @@ export class RemoteEntryComponent {} exports[`MF Remote App Generator should generate the a remote setup for standalone components 4`] = ` "import { Route } from '@angular/router'; + +export const appRoutes: Route[] = [ + {path: '', loadChildren: () => import('./remote-entry/entry.routes').then(m => m.remoteRoutes)},]" +`; + +exports[`MF Remote App Generator should generate the a remote setup for standalone components 5`] = ` +"import { Route } from '@angular/router'; import { RemoteEntryComponent } from './entry.component'; -export const RemoteRoutes: Route[] = [{path: '', component: RemoteEntryComponent}]" +export const remoteRoutes: Route[] = [{ path: '', component: RemoteEntryComponent }];" `; diff --git a/packages/angular/src/generators/remote/remote.spec.ts b/packages/angular/src/generators/remote/remote.spec.ts index e2e7c5420a6aa..5d01913afbae9 100644 --- a/packages/angular/src/generators/remote/remote.spec.ts +++ b/packages/angular/src/generators/remote/remote.spec.ts @@ -131,7 +131,10 @@ describe('MF Remote App Generator', () => { tree.read(`apps/test/src/app/remote-entry/entry.component.ts`, 'utf-8') ).toMatchSnapshot(); expect( - tree.read(`apps/test/src/app/remote-entry/routes.ts`, 'utf-8') + tree.read(`apps/test/src/app/app.routes.ts`, 'utf-8') + ).toMatchSnapshot(); + expect( + tree.read(`apps/test/src/app/remote-entry/entry.routes.ts`, 'utf-8') ).toMatchSnapshot(); }); diff --git a/packages/angular/src/generators/setup-mf/__snapshots__/setup-mf.spec.ts.snap b/packages/angular/src/generators/setup-mf/__snapshots__/setup-mf.spec.ts.snap index 0f61ab761f88c..a8c861b9325ba 100644 --- a/packages/angular/src/generators/setup-mf/__snapshots__/setup-mf.spec.ts.snap +++ b/packages/angular/src/generators/setup-mf/__snapshots__/setup-mf.spec.ts.snap @@ -10,33 +10,17 @@ exports[`Init MF --federationType=dynamic should create a host with the correct `; exports[`Init MF should add a remote application and add it to a specified host applications router config 1`] = ` -"import { NgModule } from '@angular/core'; -import { BrowserModule } from '@angular/platform-browser'; - -import { AppComponent } from './app.component'; -import { NxWelcomeComponent } from './nx-welcome.component'; -import { RouterModule } from '@angular/router'; - -@NgModule({ - declarations: [ - AppComponent, - NxWelcomeComponent - ], - imports: [ - BrowserModule, - RouterModule.forRoot([{ - path: 'remote1', - loadChildren: () => import('remote1/Module').then(m => m.RemoteEntryModule) - }, { - path: 'remote2', - loadChildren: () => import('remote2/Module').then(m => m.RemoteEntryModule) - }], {initialNavigation: 'enabledBlocking'}) - ], - providers: [], - bootstrap: [AppComponent] -}) -export class AppModule { } -" +"import { Route } from '@angular/router'; + +export const appRoutes: Route[] = [ + { + path: 'remote2', + loadChildren: () => import('remote2/Module').then(m => m.RemoteEntryModule) + }, + { + path: 'remote1', + loadChildren: () => import('remote1/Module').then(m => m.RemoteEntryModule) + }," `; exports[`Init MF should add a remote application and add it to a specified host applications webpack config that contains a remote application already 1`] = ` @@ -54,31 +38,14 @@ exports[`Init MF should add a remote application and add it to a specified host `; exports[`Init MF should add a remote to dynamic host correctly 1`] = ` -"import { NgModule } from '@angular/core'; -import { BrowserModule } from '@angular/platform-browser'; - -import { AppComponent } from './app.component'; -import { NxWelcomeComponent } from './nx-welcome.component'; -import { RouterModule } from '@angular/router'; +"import { Route } from '@angular/router'; import { loadRemoteModule } from '@nrwl/angular/mf'; -@NgModule({ - declarations: [ - AppComponent, - NxWelcomeComponent - ], - imports: [ - BrowserModule, - RouterModule.forRoot([{ - path: 'remote1', - loadChildren: () => loadRemoteModule('remote1', './Module').then(m => m.RemoteEntryModule) - }], {initialNavigation: 'enabledBlocking'}) - ], - providers: [], - bootstrap: [AppComponent] -}) -export class AppModule { } -" +export const appRoutes: Route[] = [ + { + path: 'remote1', + loadChildren: () => loadRemoteModule('remote1', './Module').then(m => m.RemoteEntryModule) + },]" `; exports[`Init MF should create webpack and mf configs correctly 1`] = ` diff --git a/packages/angular/src/generators/setup-mf/files/entry-module-files/entry.module.ts__tmpl__ b/packages/angular/src/generators/setup-mf/files/entry-module-files/entry.module.ts__tmpl__ index b7c219ea7d507..82fc12618d803 100644 --- a/packages/angular/src/generators/setup-mf/files/entry-module-files/entry.module.ts__tmpl__ +++ b/packages/angular/src/generators/setup-mf/files/entry-module-files/entry.module.ts__tmpl__ @@ -3,18 +3,14 @@ import { CommonModule } from '@angular/common';<% if(routing) { %> import { RouterModule } from '@angular/router';<% } %> import { RemoteEntryComponent } from './entry.component'; -import { NxWelcomeComponent } from './nx-welcome.component'; +import { NxWelcomeComponent } from './nx-welcome.component';<% if(routing) { %> +import { remoteRoutes } from './entry.routes';<% } %> @NgModule({ declarations: [RemoteEntryComponent, NxWelcomeComponent], imports: [ CommonModule,<% if(routing) { %> - RouterModule.forChild([ - { - path: '', - component: RemoteEntryComponent, - }, - ]),<% } %> + RouterModule.forChild(remoteRoutes),<% } %> ], providers: [],<% if(!routing) { %> exports: [RemoteEntryComponent],<% } %> diff --git a/packages/angular/src/generators/setup-mf/files/standalone-entry-component-files/routes.ts__tmpl__ b/packages/angular/src/generators/setup-mf/files/entry-module-files/entry.routes.ts__tmpl__ similarity index 54% rename from packages/angular/src/generators/setup-mf/files/standalone-entry-component-files/routes.ts__tmpl__ rename to packages/angular/src/generators/setup-mf/files/entry-module-files/entry.routes.ts__tmpl__ index d590044fa1e1e..76cdef499792b 100644 --- a/packages/angular/src/generators/setup-mf/files/standalone-entry-component-files/routes.ts__tmpl__ +++ b/packages/angular/src/generators/setup-mf/files/entry-module-files/entry.routes.ts__tmpl__ @@ -1,4 +1,4 @@ import { Route } from '@angular/router'; import { RemoteEntryComponent } from './entry.component'; -export const RemoteRoutes: Route[] = [{path: '', component: RemoteEntryComponent}] \ No newline at end of file +export const remoteRoutes: Route[] = [{ path: '', component: RemoteEntryComponent }]; \ No newline at end of file diff --git a/packages/angular/src/generators/setup-mf/files/standalone-entry-component-files/entry.routes.ts__tmpl__ b/packages/angular/src/generators/setup-mf/files/standalone-entry-component-files/entry.routes.ts__tmpl__ new file mode 100644 index 0000000000000..76cdef499792b --- /dev/null +++ b/packages/angular/src/generators/setup-mf/files/standalone-entry-component-files/entry.routes.ts__tmpl__ @@ -0,0 +1,4 @@ +import { Route } from '@angular/router'; +import { RemoteEntryComponent } from './entry.component'; + +export const remoteRoutes: Route[] = [{ path: '', component: RemoteEntryComponent }]; \ No newline at end of file diff --git a/packages/angular/src/generators/setup-mf/files/webpack/module-federation.config.js__tmpl__ b/packages/angular/src/generators/setup-mf/files/webpack/module-federation.config.js__tmpl__ index 005462031803d..420e05622c210 100644 --- a/packages/angular/src/generators/setup-mf/files/webpack/module-federation.config.js__tmpl__ +++ b/packages/angular/src/generators/setup-mf/files/webpack/module-federation.config.js__tmpl__ @@ -2,7 +2,7 @@ module.exports = { name: '<%= name %>',<% if(type === 'host') { %> remotes: [<% remotes.forEach(function(remote) { %>'<%= remote.remoteName %>',<% }); %>]<% } %><% if(type === 'remote') { %> exposes: {<% if(standalone) { %> - './Routes': '<%= projectRoot %>/src/app/remote-entry/routes.ts',<% } else { %> + './Routes': '<%= projectRoot %>/src/app/remote-entry/entry.routes.ts',<% } else { %> './Module': '<%= projectRoot %>/src/app/remote-entry/entry.module.ts',<% } %> },<% } %> } \ No newline at end of file diff --git a/packages/angular/src/generators/setup-mf/lib/add-remote-entry.ts b/packages/angular/src/generators/setup-mf/lib/add-remote-entry.ts index abcfd81909b01..03313f4cca5c5 100644 --- a/packages/angular/src/generators/setup-mf/lib/add-remote-entry.ts +++ b/packages/angular/src/generators/setup-mf/lib/add-remote-entry.ts @@ -5,6 +5,7 @@ import { readWorkspaceConfiguration, } from '@nrwl/devkit'; import type { Schema } from '../schema'; +import { addRoute } from '../../../utils/nx-devkit/route-utils'; export function addRemoteEntry( tree: Tree, @@ -30,7 +31,21 @@ export function addRemoteEntry( } ); - if (!standalone) { + if (standalone && routing) { + addRoute( + tree, + joinPathFragments(appRoot, 'src/app/app.routes.ts'), + `{path: '', loadChildren: () => import('./remote-entry/entry.routes').then(m => m.remoteRoutes)}` + ); + } else { + if (routing) { + addRoute( + tree, + joinPathFragments(appRoot, 'src/app/app.routes.ts'), + `{path: '', loadChildren: () => import('./remote-entry/entry.module').then(m => m.RemoteEntryModule)}` + ); + } + tree.write( `${appRoot}/src/app/app.module.ts`, `/* diff --git a/packages/angular/src/generators/setup-mf/lib/add-remote-to-host.ts b/packages/angular/src/generators/setup-mf/lib/add-remote-to-host.ts index 3af48f96f8ba9..eb9e53cbec9fe 100644 --- a/packages/angular/src/generators/setup-mf/lib/add-remote-to-host.ts +++ b/packages/angular/src/generators/setup-mf/lib/add-remote-to-host.ts @@ -10,9 +10,8 @@ import type { Schema } from '../schema'; import { tsquery } from '@phenomnomnominal/tsquery'; import * as ts from 'typescript'; import { ArrayLiteralExpression } from 'typescript'; -import { addRoute } from '../../../utils/nx-devkit/ast-utils'; import { insertImport } from '@nrwl/workspace/src/utilities/ast-utils'; -import { addStandaloneRoute } from '../../../utils/nx-devkit/standalone-utils'; +import { addRoute } from '../../../utils/nx-devkit/route-utils'; export function checkIsCommaNeeded(mfRemoteText: string) { const remoteText = mfRemoteText.replace(/\s+/g, ''); @@ -120,22 +119,14 @@ function addLazyLoadedRouteToHostAppModule( hostFederationType: 'dynamic' | 'static' ) { const hostAppConfig = readProjectConfiguration(tree, options.host); - const isHostStandalone = !tree - .read(joinPathFragments(hostAppConfig.sourceRoot, 'bootstrap.ts'), 'utf-8') - .includes('bootstrapModule'); - const pathToHostRootRouting = isHostStandalone - ? `${hostAppConfig.sourceRoot}/bootstrap.ts` - : `${hostAppConfig.sourceRoot}/app/app.module.ts`; + const pathToHostRootRouting = `${hostAppConfig.sourceRoot}/app/app.routes.ts`; if (!tree.exists(pathToHostRootRouting)) { return; } const hostRootRoutingFile = tree.read(pathToHostRootRouting, 'utf-8'); - if (!hostRootRoutingFile.includes('RouterModule.forRoot(')) { - return; - } let sourceFile = ts.createSourceFile( pathToHostRootRouting, @@ -155,31 +146,22 @@ function addLazyLoadedRouteToHostAppModule( } const routePathName = options.standalone ? 'Routes' : 'Module'; + const exportedRemote = options.standalone + ? 'remoteRoutes' + : 'RemoteEntryModule'; const routeToAdd = hostFederationType === 'dynamic' ? `loadRemoteModule('${options.appName}', './${routePathName}')` : `import('${options.appName}/${routePathName}')`; - if (hostRootRoutingFile.includes('@NgModule')) { - sourceFile = addRoute( - tree, - pathToHostRootRouting, - sourceFile, - `{ - path: '${options.appName}', - loadChildren: () => ${routeToAdd}.then(m => m.RemoteEntryModule) - }` - ); - } else { - addStandaloneRoute( - tree, - pathToHostRootRouting, - `{ + addRoute( + tree, + pathToHostRootRouting, + `{ path: '${options.appName}', - loadChildren: () => ${routeToAdd}.then(m => m.RemoteRoutes) + loadChildren: () => ${routeToAdd}.then(m => m.${exportedRemote}) }` - ); - } + ); const pathToAppComponentTemplate = joinPathFragments( hostAppConfig.sourceRoot, diff --git a/packages/angular/src/generators/setup-mf/lib/fix-bootstrap.ts b/packages/angular/src/generators/setup-mf/lib/fix-bootstrap.ts index 5bd0532747dca..dd8d3ac1c2a0e 100644 --- a/packages/angular/src/generators/setup-mf/lib/fix-bootstrap.ts +++ b/packages/angular/src/generators/setup-mf/lib/fix-bootstrap.ts @@ -33,7 +33,7 @@ import {enableProdMode, importProvidersFrom} from "@angular/core"; import {bootstrapApplication} from "@angular/platform-browser"; import {RouterModule} from "@angular/router"; import {RemoteEntryComponent} from "./app/remote-entry/entry.component"; -import {RemoteRoutes} from "./app/remote-entry/routes"; +import {appRoutes} from "./app/app.routes"; if (environment.production) { enableProdMode(); @@ -42,7 +42,7 @@ if (environment.production) { bootstrapApplication(RemoteEntryComponent, { providers: [ importProvidersFrom( - RouterModule.forRoot(RemoteRoutes, {initialNavigation: 'enabledBlocking'}) + RouterModule.forRoot(appRoutes, {initialNavigation: 'enabledBlocking'}) ) ] });`; diff --git a/packages/angular/src/generators/setup-mf/setup-mf.spec.ts b/packages/angular/src/generators/setup-mf/setup-mf.spec.ts index 0bb4557b993ce..8650ae872a1c4 100644 --- a/packages/angular/src/generators/setup-mf/setup-mf.spec.ts +++ b/packages/angular/src/generators/setup-mf/setup-mf.spec.ts @@ -262,8 +262,8 @@ describe('Init MF', () => { }); // ASSERT - const hostAppModule = tree.read('apps/app1/src/app/app.module.ts', 'utf-8'); - expect(hostAppModule).toMatchSnapshot(); + const hostAppRoutes = tree.read('apps/app1/src/app/app.routes.ts', 'utf-8'); + expect(hostAppRoutes).toMatchSnapshot(); }); it('should modify the associated cypress project to add the workaround correctly', async () => { @@ -340,7 +340,7 @@ describe('Init MF', () => { remote1: 'http://localhost:4201', }); expect( - tree.read('apps/app1/src/app/app.module.ts', 'utf-8') + tree.read('apps/app1/src/app/app.routes.ts', 'utf-8') ).toMatchSnapshot(); }); }); diff --git a/packages/angular/src/utils/nx-devkit/ast-utils.ts b/packages/angular/src/utils/nx-devkit/ast-utils.ts index 7af6480af255f..64bc6bd1bf713 100644 --- a/packages/angular/src/utils/nx-devkit/ast-utils.ts +++ b/packages/angular/src/utils/nx-devkit/ast-utils.ts @@ -2,11 +2,11 @@ import * as ts from 'typescript'; import { findNodes } from '@nrwl/workspace/src/utilities/typescript/find-nodes'; import { getSourceNodes } from '@nrwl/workspace/src/utilities/typescript/get-source-nodes'; import * as path from 'path'; -import { Tree, names, readProjectConfiguration } from '@nrwl/devkit'; +import { names, readProjectConfiguration, Tree } from '@nrwl/devkit'; import { + getImport, insertChange, removeChange, - getImport, replaceChange, } from '@nrwl/workspace/src/utilities/ast-utils'; @@ -458,7 +458,7 @@ function getMatchingProperty( return getMatchingObjectLiteralElement(node, source, property); } -export function addRoute( +export function addRouteToNgModule( host: Tree, ngModulePath: string, source: ts.SourceFile, diff --git a/packages/angular/src/utils/nx-devkit/route-utils.spec.ts b/packages/angular/src/utils/nx-devkit/route-utils.spec.ts new file mode 100644 index 0000000000000..14e3975adb901 --- /dev/null +++ b/packages/angular/src/utils/nx-devkit/route-utils.spec.ts @@ -0,0 +1,56 @@ +import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing'; +import { addRoute } from './route-utils'; + +describe('standalone component utils', () => { + it('should add a static route to the routes file', () => { + // ARRANGE + const tree = createTreeWithEmptyWorkspace(); + tree.write( + 'routes-file.ts', + `import { Route } from '@angular/router'; + export const ROUTES: Route[] = [];` + ); + + // ACT + addRoute( + tree, + 'routes-file.ts', + "{path: 'test', children: ROUTES }", + false, + 'ROUTES', + '@proj/lib' + ); + + // ASSERT + expect(tree.read('routes-file.ts', 'utf-8')).toMatchInlineSnapshot(` + "import { Route } from '@angular/router'; + import { ROUTES } from '@proj/lib'; + export const ROUTES: Route[] = [ + {path: 'test', children: ROUTES },]" + `); + }); + + it('should add a lazy route to the routes file', () => { + // ARRANGE + const tree = createTreeWithEmptyWorkspace(); + tree.write( + 'routes-file.ts', + `import { Route } from '@angular/router'; + export const ROUTES: Route[] = [];` + ); + + // ACT + addRoute( + tree, + 'routes-file.ts', + "{path: 'test', , loadChildren: () => import('@proj/lib').then(m => m.ROUTES) }" + ); + + // ASSERT + expect(tree.read('routes-file.ts', 'utf-8')).toMatchInlineSnapshot(` + "import { Route } from '@angular/router'; + export const ROUTES: Route[] = [ + {path: 'test', , loadChildren: () => import('@proj/lib').then(m => m.ROUTES) },]" + `); + }); +}); diff --git a/packages/angular/src/utils/nx-devkit/route-utils.ts b/packages/angular/src/utils/nx-devkit/route-utils.ts new file mode 100644 index 0000000000000..3c67ffaba7bc4 --- /dev/null +++ b/packages/angular/src/utils/nx-devkit/route-utils.ts @@ -0,0 +1,80 @@ +import { Tree } from '@nrwl/devkit'; +import { tsquery } from '@phenomnomnominal/tsquery'; +import * as ts from 'typescript'; +import { insertImport } from '@nrwl/workspace/src/utilities/ast-utils'; +import { addRouteToNgModule } from './ast-utils'; + +export function addRoute( + tree: Tree, + routesFile: string, + route: string, + lazy: boolean = true, + routesConst?: string, + importPath?: string +) { + if (!tree.exists(routesFile)) { + throw new Error( + `Path to parent routing declaration (${routesFile}) does not exist. Please ensure path is correct.` + ); + } + + let routesFileContents = tree.read(routesFile, 'utf-8'); + + if (!lazy) { + let parentSourceFile = ts.createSourceFile( + routesFile, + routesFileContents, + ts.ScriptTarget.Latest, + true + ); + + parentSourceFile = insertImport( + tree, + parentSourceFile, + routesFile, + routesConst, + importPath + ); + + routesFileContents = tree.read(routesFile, 'utf-8'); + } + + const ast = tsquery.ast(routesFileContents); + + const ROUTES_ARRAY_SELECTOR = + 'VariableDeclaration:has(ArrayType > TypeReference > Identifier[name=Route]) > ArrayLiteralExpression'; + + const routesArrayNodes = tsquery(ast, ROUTES_ARRAY_SELECTOR, { + visitAllChildren: true, + }); + const isRoutesArray = routesArrayNodes.length > 0; + + if (!isRoutesArray) { + if (routesFileContents.includes('@NgModule')) { + const sourceFile = ts.createSourceFile( + routesFile, + routesFileContents, + ts.ScriptTarget.Latest, + true + ); + + addRouteToNgModule(tree, routesFile, sourceFile, route); + return; + } else { + throw new Error( + `Routing file (${routesFile}) does not a routing configuration. Please ensure the parent contains a routing configuration.` + ); + } + } + + const newRoutesFileContents = `${routesFileContents.slice( + 0, + routesArrayNodes[0].getStart() + 1 + )} + ${route},${routesFileContents.slice( + routesArrayNodes[0].getStart() + 1, + -1 + )}`; + + tree.write(routesFile, newRoutesFileContents); +} diff --git a/packages/angular/src/utils/nx-devkit/standalone-utils.spec.ts b/packages/angular/src/utils/nx-devkit/standalone-utils.spec.ts deleted file mode 100644 index 2256d93901b69..0000000000000 --- a/packages/angular/src/utils/nx-devkit/standalone-utils.spec.ts +++ /dev/null @@ -1,144 +0,0 @@ -import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing'; -import { addStandaloneRoute } from './standalone-utils'; - -describe('standalone component utils', () => { - it('should add a lazy route correctly to parent with router module', () => { - // ARRANGE - const tree = createTreeWithEmptyWorkspace(); - tree.write( - 'parent-file.ts', - `import { RouterModule } from '@angular/router'; - import { AppComponent } from './app/app.component'; - - bootstrapApplication(AppComponent, { - providers: [ - importProvidersFrom( - RouterModule.forRoot([], { initialNavigation: 'enabledBlocking' }), - SomeOtherModule - ), - ], - })` - ); - - // ACT - addStandaloneRoute( - tree, - 'parent-file.ts', - "{path: 'test', loadChildren: () => import('@proj/lib').then(m => m.ROUTES) }" - ); - - // ASSERT - expect(tree.read('parent-file.ts', 'utf-8')).toMatchInlineSnapshot(` - "import { RouterModule } from '@angular/router'; - import { AppComponent } from './app/app.component'; - - bootstrapApplication(AppComponent, { - providers: [ - importProvidersFrom( - RouterModule.forRoot([ - {path: 'test', loadChildren: () => import('@proj/lib').then(m => m.ROUTES) },], { initialNavigation: 'enabledBlocking' }), - SomeOtherModule - ), - ], - }" - `); - }); - - it('should add a route correctly to parent with router module', () => { - // ARRANGE - const tree = createTreeWithEmptyWorkspace(); - tree.write( - 'parent-file.ts', - `import { RouterModule } from '@angular/router'; - import { AppComponent } from './app/app.component'; - - bootstrapApplication(AppComponent, { - providers: [ - importProvidersFrom( - RouterModule.forRoot([], { initialNavigation: 'enabledBlocking' }), - SomeOtherModule - ), - ], - })` - ); - - // ACT - addStandaloneRoute( - tree, - 'parent-file.ts', - "{path: 'test', children: ROUTES }", - false, - 'ROUTES', - '@proj/lib' - ); - - // ASSERT - expect(tree.read('parent-file.ts', 'utf-8')).toMatchInlineSnapshot(` - "import { RouterModule } from '@angular/router'; - import { AppComponent } from './app/app.component'; - import { ROUTES } from '@proj/lib'; - - bootstrapApplication(AppComponent, { - providers: [ - importProvidersFrom( - RouterModule.forRoot([ - {path: 'test', children: ROUTES },], { initialNavigation: 'enabledBlocking' }), - SomeOtherModule - ), - ], - }" - `); - }); - - it('should add a lazy route correctly to parent with route config', () => { - // ARRANGE - const tree = createTreeWithEmptyWorkspace(); - tree.write( - 'parent-file.ts', - `import { Route } from '@angular/router'; - export const ROUTES: Route[] = [];` - ); - - // ACT - addStandaloneRoute( - tree, - 'parent-file.ts', - "{path: 'test', loadChildren: () => import('@proj/lib').then(m => m.ROUTES) }" - ); - - // ASSERT - expect(tree.read('parent-file.ts', 'utf-8')).toMatchInlineSnapshot(` - "import { Route } from '@angular/router'; - export const ROUTES: Route[] = [ - {path: 'test', loadChildren: () => import('@proj/lib').then(m => m.ROUTES) },]" - `); - }); - - it('should add a route correctly to parent with router module', () => { - // ARRANGE - const tree = createTreeWithEmptyWorkspace(); - tree.write( - 'parent-file.ts', - `import { Route } from '@angular/router'; - export const ROUTES: Route[] = [];` - ); - - // ACT - addStandaloneRoute( - tree, - 'parent-file.ts', - "{path: 'test', children: ROUTES }", - false, - 'ROUTES', - '@proj/lib' - ); - - // ASSERT - expect(tree.read('parent-file.ts', 'utf-8')).toMatchInlineSnapshot(` - "import { Route } from '@angular/router'; - import { ROUTES } from '@proj/lib'; - export const ROUTES: Route[] = [ - {path: 'test', children: ROUTES },]" - `); - }); -}); diff --git a/packages/angular/src/utils/nx-devkit/standalone-utils.ts b/packages/angular/src/utils/nx-devkit/standalone-utils.ts deleted file mode 100644 index 634e975cc4697..0000000000000 --- a/packages/angular/src/utils/nx-devkit/standalone-utils.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { Tree } from '@nrwl/devkit'; -import { tsquery } from '@phenomnomnominal/tsquery'; -import * as ts from 'typescript'; -import { insertImport } from '@nrwl/workspace/src/utilities/ast-utils'; - -export function addStandaloneRoute( - tree: Tree, - parentPath: string, - route: string, - lazy: boolean = true, - routesConst?: string, - importPath?: string -) { - if (!tree.exists(parentPath)) { - throw new Error( - `Path to parent routing declaration (${parentPath}) does not exist. Please ensure path is correct.` - ); - } - - let parentContents = tree.read(parentPath, 'utf-8'); - - if (!lazy) { - let parentSourceFile = ts.createSourceFile( - parentPath, - parentContents, - ts.ScriptTarget.Latest, - true - ); - - parentSourceFile = insertImport( - tree, - parentSourceFile, - parentPath, - routesConst, - importPath - ); - - parentContents = tree.read(parentPath, 'utf-8'); - } - - const ast = tsquery.ast(parentContents); - - const IMPORT_PROVIDERS_FROM_ROUTER_MODULE_SELECTOR = - 'CallExpression:has(Identifier[name=importProvidersFrom]) CallExpression:has(PropertyAccessExpression:has(Identifier[name=RouterModule])) > ArrayLiteralExpression'; - - const ROUTES_ARRAY_SELECTOR = - 'VariableDeclaration:has(ArrayType > TypeReference > Identifier[name=Route]) > ArrayLiteralExpression'; - - const routerModuleNodes = tsquery( - ast, - IMPORT_PROVIDERS_FROM_ROUTER_MODULE_SELECTOR, - { visitAllChildren: true } - ); - const isImportProvidersFromRouterSetup = routerModuleNodes.length > 0; - - const routesArrayNodes = tsquery(ast, ROUTES_ARRAY_SELECTOR, { - visitAllChildren: true, - }); - const isRoutesArray = routesArrayNodes.length > 0; - - if (!isImportProvidersFromRouterSetup && !isRoutesArray) { - throw new Error( - 'Parent routing declaration does not contain a routing configuration. Please ensure the parent contains a routing configuration.' - ); - } - - const nodes = isImportProvidersFromRouterSetup - ? routerModuleNodes - : routesArrayNodes; - - const newParentContents = `${parentContents.slice(0, nodes[0].getStart() + 1)} - ${route},${parentContents.slice(nodes[0].getStart() + 1, -1)}`; - - tree.write(parentPath, newParentContents); -}