Skip to content

Commit

Permalink
fix(angular): module federation generation should match react (#10214)
Browse files Browse the repository at this point in the history
  • Loading branch information
Coly010 committed May 9, 2022
1 parent 425adf1 commit e707461
Show file tree
Hide file tree
Showing 11 changed files with 137 additions and 32 deletions.
4 changes: 4 additions & 0 deletions docs/generated/packages/angular.json
Expand Up @@ -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 `<appName>-e2e`."
},
"prefix": {
"type": "string",
"description": "The prefix to use for any generated component."
}
},
"required": ["appName", "mfeType"],
Expand Down
1 change: 1 addition & 0 deletions packages/angular/src/generators/application/lib/add-mfe.ts
Expand Up @@ -15,5 +15,6 @@ export async function addMfe(host: Tree, options: NormalizedSchema) {
skipPackageJson: options.skipPackageJson,
e2eProjectName: options.e2eProjectName,
federationType: options.federationType,
prefix: options.prefix,
});
}
13 changes: 12 additions & 1 deletion packages/angular/src/generators/host/host.spec.ts
Expand Up @@ -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);

Expand All @@ -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(`
"<ul class=\\"remote-menu\\">
<li><a routerLink='/'>Home</a></li>
<li><a routerLink='remote1'>Remote1</a></li>
<li><a routerLink='remote2'>Remote2</a></li>
</ul>
<router-outlet></router-outlet>
"
`);
});

it('should generate a host, integrate existing remotes and generate any remotes that dont exist', async () => {
Expand Down
71 changes: 68 additions & 3 deletions 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);
Expand Down Expand Up @@ -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<li><a routerLink='${normalizeProjectName(
remote,
options.directory
)}'>${names(remote).className}</a></li>`,
''
)
: '';

tree.write(
joinPathFragments(sourceRoot, 'app/app.component.html'),
`<ul class="remote-menu">
<li><a routerLink='/'>Home</a></li>
${remoteRoutes}
</ul>
<router-outlet></router-outlet>
`
);

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
}`
);
}
16 changes: 12 additions & 4 deletions 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';
Expand Down Expand Up @@ -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')
Expand Down
Expand Up @@ -2,12 +2,6 @@ import { Component } from '@angular/core';

@Component({
selector: '<%= appName %>-entry',
template: `<div class="remote-entry"><h2><%= appName %>'s Remote Entry Component</h2></div>`,
styles: [`
.remote-entry {
background-color: #143055;
color: white;
padding: 5px;
}`]
template: `<<%= prefix %>-nx-welcome></<%= prefix %>-nx-welcome>`
})
export class RemoteEntryComponent {}
Expand Up @@ -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([
Expand Down
@@ -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') {
Expand All @@ -20,6 +16,7 @@ export function addEntryModule(
tmpl: '',
appName,
routing,
prefix,
}
);

Expand Down
39 changes: 29 additions & 10 deletions 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) {
Expand Down Expand Up @@ -41,7 +42,7 @@ export function addRemoteToHost(tree: Tree, options: Schema) {

const declarationFilePath = joinPathFragments(
hostProject.sourceRoot,
'decl.d.ts'
'remotes.d.ts'
);

const declarationFileContent =
Expand Down Expand Up @@ -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(`<ul class="remote-menu">`) &&
appComponent.includes('</ul>')
) {
const indexOfClosingMenuTag = appComponent.indexOf('</ul>');
const newAppComponent = `${appComponent.slice(
0,
indexOfClosingMenuTag
)}<li><a routerLink='${options.appName}'>${
names(options.appName).className
}</a></li>\n${appComponent.slice(indexOfClosingMenuTag)}`;
tree.write(pathToAppComponentTemplate, newAppComponent);
}
}
1 change: 1 addition & 0 deletions packages/angular/src/generators/setup-mfe/schema.d.ts
Expand Up @@ -9,4 +9,5 @@ export interface Schema {
skipFormat?: boolean;
skipPackageJson?: boolean;
e2eProjectName?: string;
prefix?: string;
}
4 changes: 4 additions & 0 deletions packages/angular/src/generators/setup-mfe/schema.json
Expand Up @@ -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 `<appName>-e2e`."
},
"prefix": {
"type": "string",
"description": "The prefix to use for any generated component."
}
},
"required": ["appName", "mfeType"],
Expand Down

1 comment on commit e707461

@vercel
Copy link

@vercel vercel bot commented on e707461 May 9, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

nx-dev – ./

nx-dev-git-master-nrwl.vercel.app
nx-dev-nrwl.vercel.app
nx.dev
nx-five.vercel.app

Please sign in to comment.