From e7074617bec07ec849aa9766a5b2a63aa06518b9 Mon Sep 17 00:00:00 2001 From: Colum Ferry Date: Mon, 9 May 2022 15:31:57 +0100 Subject: [PATCH] fix(angular): module federation generation should match react (#10214) --- docs/generated/packages/angular.json | 4 ++ .../src/generators/application/lib/add-mfe.ts | 1 + .../angular/src/generators/host/host.spec.ts | 13 +++- packages/angular/src/generators/host/host.ts | 71 ++++++++++++++++++- .../angular/src/generators/remote/remote.ts | 16 +++-- .../entry.component.ts__tmpl__ | 8 +-- .../entry.module.ts__tmpl__ | 3 +- .../setup-mfe/lib/add-entry-module.ts | 9 +-- .../setup-mfe/lib/add-remote-to-host.ts | 39 +++++++--- .../src/generators/setup-mfe/schema.d.ts | 1 + .../src/generators/setup-mfe/schema.json | 4 ++ 11 files changed, 137 insertions(+), 32 deletions(-) diff --git a/docs/generated/packages/angular.json b/docs/generated/packages/angular.json index a3fb7510869b8..a39f8b9f5eddc 100644 --- a/docs/generated/packages/angular.json +++ b/docs/generated/packages/angular.json @@ -1772,6 +1772,10 @@ "e2eProjectName": { "type": "string", "description": "The project name of the associated E2E project for the application. This is only required for Cypress E2E projects that do not follow the naming convention `-e2e`." + }, + "prefix": { + "type": "string", + "description": "The prefix to use for any generated component." } }, "required": ["appName", "mfeType"], diff --git a/packages/angular/src/generators/application/lib/add-mfe.ts b/packages/angular/src/generators/application/lib/add-mfe.ts index 4310fa0db159c..070c0cd394639 100644 --- a/packages/angular/src/generators/application/lib/add-mfe.ts +++ b/packages/angular/src/generators/application/lib/add-mfe.ts @@ -15,5 +15,6 @@ export async function addMfe(host: Tree, options: NormalizedSchema) { skipPackageJson: options.skipPackageJson, e2eProjectName: options.e2eProjectName, federationType: options.federationType, + prefix: options.prefix, }); } diff --git a/packages/angular/src/generators/host/host.spec.ts b/packages/angular/src/generators/host/host.spec.ts index d2515fbba4555..5d5f6d6fa65a4 100644 --- a/packages/angular/src/generators/host/host.spec.ts +++ b/packages/angular/src/generators/host/host.spec.ts @@ -42,7 +42,7 @@ describe('Host App Generator', () => { expect(tree.read('apps/test/webpack.config.js', 'utf-8')).toMatchSnapshot(); }); - it('should generate a host and any remotes that dont exist', async () => { + it('should generate a host and any remotes that dont exist with correct routing setup', async () => { // ARRANGE const tree = createTreeWithEmptyWorkspace(2); @@ -59,6 +59,17 @@ describe('Host App Generator', () => { expect( tree.read('apps/host-app/module-federation.config.js', 'utf-8') ).toContain(`'remote1','remote2'`); + expect(tree.read('apps/host-app/src/app/app.component.html', 'utf-8')) + .toMatchInlineSnapshot(` + " + + " + `); }); it('should generate a host, integrate existing remotes and generate any remotes that dont exist', async () => { diff --git a/packages/angular/src/generators/host/host.ts b/packages/angular/src/generators/host/host.ts index 37cfb1dd7d571..6c1e936824046 100644 --- a/packages/angular/src/generators/host/host.ts +++ b/packages/angular/src/generators/host/host.ts @@ -1,10 +1,17 @@ -import { formatFiles, names, Tree } from '@nrwl/devkit'; +import { + formatFiles, + getProjects, + joinPathFragments, + names, + readProjectConfiguration, + Tree, +} from '@nrwl/devkit'; import type { Schema } from './schema'; - -import { getProjects } from '@nrwl/devkit'; 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'; export default async function host(tree: Tree, options: Schema) { const projects = getProjects(tree); @@ -42,9 +49,67 @@ export default async function host(tree: Tree, options: Schema) { }); } + routeToNxWelcome(tree, options); + if (!options.skipFormat) { await formatFiles(tree); } return installTask; } + +function routeToNxWelcome(tree: Tree, options: Schema) { + const { sourceRoot } = readProjectConfiguration( + tree, + normalizeProjectName(options.name, options.directory) + ); + + const remoteRoutes = + options.remotes && Array.isArray(options.remotes) + ? options.remotes.reduce( + (routes, remote) => + `${routes}\n
  • ${names(remote).className}
  • `, + '' + ) + : ''; + + tree.write( + joinPathFragments(sourceRoot, 'app/app.component.html'), + ` + +` + ); + + const pathToHostAppModule = joinPathFragments( + sourceRoot, + 'app/app.module.ts' + ); + const hostAppModule = tree.read(pathToHostAppModule, 'utf-8'); + + if (!hostAppModule.includes('RouterModule.forRoot(')) { + return; + } + + let sourceFile = ts.createSourceFile( + pathToHostAppModule, + hostAppModule, + ts.ScriptTarget.Latest, + true + ); + + sourceFile = addRoute( + tree, + pathToHostAppModule, + sourceFile, + `{ + path: '', + component: NxWelcomeComponent + }` + ); +} diff --git a/packages/angular/src/generators/remote/remote.ts b/packages/angular/src/generators/remote/remote.ts index f1d9f9b87e144..ed5226c9362a8 100644 --- a/packages/angular/src/generators/remote/remote.ts +++ b/packages/angular/src/generators/remote/remote.ts @@ -1,6 +1,10 @@ -import { joinPathFragments, Tree } from '@nrwl/devkit'; +import { + getProjects, + joinPathFragments, + readProjectConfiguration, + Tree, +} from '@nrwl/devkit'; import type { Schema } from './schema'; -import { getProjects, readProjectConfiguration } from '@nrwl/devkit'; import applicationGenerator from '../application/application'; import { getMFProjects } from '../../utils/get-mf-projects'; import { normalizeProjectName } from '../utils/project'; @@ -56,8 +60,12 @@ function removeDeadCode(tree: Tree, options: Schema) { } }); - tree.delete( - joinPathFragments(project.sourceRoot, 'app/nx-welcome.component.ts') + tree.rename( + joinPathFragments(project.sourceRoot, 'app/nx-welcome.component.ts'), + joinPathFragments( + project.sourceRoot, + 'app/remote-entry/nx-welcome.component.ts' + ) ); tree.delete( joinPathFragments(project.sourceRoot, 'app/app.component.spec.ts') diff --git a/packages/angular/src/generators/setup-mfe/files/entry-module-files/entry.component.ts__tmpl__ b/packages/angular/src/generators/setup-mfe/files/entry-module-files/entry.component.ts__tmpl__ index 05063986ac261..888291adeab87 100644 --- a/packages/angular/src/generators/setup-mfe/files/entry-module-files/entry.component.ts__tmpl__ +++ b/packages/angular/src/generators/setup-mfe/files/entry-module-files/entry.component.ts__tmpl__ @@ -2,12 +2,6 @@ import { Component } from '@angular/core'; @Component({ selector: '<%= appName %>-entry', - template: `

    <%= appName %>'s Remote Entry Component

    `, - styles: [` - .remote-entry { - background-color: #143055; - color: white; - padding: 5px; - }`] + template: `<<%= prefix %>-nx-welcome>-nx-welcome>` }) export class RemoteEntryComponent {} diff --git a/packages/angular/src/generators/setup-mfe/files/entry-module-files/entry.module.ts__tmpl__ b/packages/angular/src/generators/setup-mfe/files/entry-module-files/entry.module.ts__tmpl__ index a994f5379f9f1..ac5775bdc3547 100644 --- a/packages/angular/src/generators/setup-mfe/files/entry-module-files/entry.module.ts__tmpl__ +++ b/packages/angular/src/generators/setup-mfe/files/entry-module-files/entry.module.ts__tmpl__ @@ -3,9 +3,10 @@ import { CommonModule } from '@angular/common'; <% if(routing) { %>import { RouterModule } from '@angular/router';<% } %> import { RemoteEntryComponent } from './entry.component'; +import { NxWelcomeComponent } from './nx-welcome.component'; @NgModule({ - declarations: [RemoteEntryComponent], + declarations: [RemoteEntryComponent, NxWelcomeComponent], imports: [ CommonModule, <% if(routing) { %>RouterModule.forChild([ diff --git a/packages/angular/src/generators/setup-mfe/lib/add-entry-module.ts b/packages/angular/src/generators/setup-mfe/lib/add-entry-module.ts index 81f539083480c..74e573b1e7b3e 100644 --- a/packages/angular/src/generators/setup-mfe/lib/add-entry-module.ts +++ b/packages/angular/src/generators/setup-mfe/lib/add-entry-module.ts @@ -1,14 +1,10 @@ import type { Tree } from '@nrwl/devkit'; +import { generateFiles, joinPathFragments } from '@nrwl/devkit'; import type { Schema } from '../schema'; -import { - generateFiles, - joinPathFragments, - readWorkspaceConfiguration, -} from '@nrwl/devkit'; export function addEntryModule( host: Tree, - { appName, routing, mfeType }: Schema, + { appName, routing, mfeType, prefix }: Schema, appRoot: string ) { if (mfeType === 'remote') { @@ -20,6 +16,7 @@ export function addEntryModule( tmpl: '', appName, routing, + prefix, } ); diff --git a/packages/angular/src/generators/setup-mfe/lib/add-remote-to-host.ts b/packages/angular/src/generators/setup-mfe/lib/add-remote-to-host.ts index a5ed340342dd8..e06b24e8771c2 100644 --- a/packages/angular/src/generators/setup-mfe/lib/add-remote-to-host.ts +++ b/packages/angular/src/generators/setup-mfe/lib/add-remote-to-host.ts @@ -1,15 +1,16 @@ -import { ProjectConfiguration, Tree, updateJson } from '@nrwl/devkit'; +import { + joinPathFragments, + names, + ProjectConfiguration, + readProjectConfiguration, + Tree, + updateJson, +} from '@nrwl/devkit'; import type { Schema } from '../schema'; - -import { readProjectConfiguration, joinPathFragments } from '@nrwl/devkit'; import { tsquery } from '@phenomnomnominal/tsquery'; -import { ArrayLiteralExpression } from 'typescript'; -import { - addImportToModule, - addRoute, -} from '../../../utils/nx-devkit/ast-utils'; - 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'; export function checkIsCommaNeeded(mfeRemoteText: string) { @@ -41,7 +42,7 @@ export function addRemoteToHost(tree: Tree, options: Schema) { const declarationFilePath = joinPathFragments( hostProject.sourceRoot, - 'decl.d.ts' + 'remotes.d.ts' ); const declarationFileContent = @@ -155,4 +156,22 @@ function addLazyLoadedRouteToHostAppModule( loadChildren: () => ${routeToAdd}.then(m => m.RemoteEntryModule) }` ); + const pathToAppComponentTemplate = joinPathFragments( + hostAppConfig.sourceRoot, + 'app/app.component.html' + ); + const appComponent = tree.read(pathToAppComponentTemplate, 'utf-8'); + if ( + appComponent.includes(`') + ) { + const indexOfClosingMenuTag = appComponent.indexOf(''); + const newAppComponent = `${appComponent.slice( + 0, + indexOfClosingMenuTag + )}
  • ${ + names(options.appName).className + }
  • \n${appComponent.slice(indexOfClosingMenuTag)}`; + tree.write(pathToAppComponentTemplate, newAppComponent); + } } diff --git a/packages/angular/src/generators/setup-mfe/schema.d.ts b/packages/angular/src/generators/setup-mfe/schema.d.ts index 825e2e76d390a..f88c58b66a3a8 100644 --- a/packages/angular/src/generators/setup-mfe/schema.d.ts +++ b/packages/angular/src/generators/setup-mfe/schema.d.ts @@ -9,4 +9,5 @@ export interface Schema { skipFormat?: boolean; skipPackageJson?: boolean; e2eProjectName?: string; + prefix?: string; } diff --git a/packages/angular/src/generators/setup-mfe/schema.json b/packages/angular/src/generators/setup-mfe/schema.json index b4da1bf9ec786..8eddc88882800 100644 --- a/packages/angular/src/generators/setup-mfe/schema.json +++ b/packages/angular/src/generators/setup-mfe/schema.json @@ -55,6 +55,10 @@ "e2eProjectName": { "type": "string", "description": "The project name of the associated E2E project for the application. This is only required for Cypress E2E projects that do not follow the naming convention `-e2e`." + }, + "prefix": { + "type": "string", + "description": "The prefix to use for any generated component." } }, "required": ["appName", "mfeType"],