Skip to content

Commit 3e6a315

Browse files
authored
feat(angular): add an serve-mfe target to host app (#6444)
1 parent c8bd620 commit 3e6a315

File tree

5 files changed

+177
-9
lines changed

5 files changed

+177
-9
lines changed

packages/angular/src/generators/application/__snapshots__/application.spec.ts.snap

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ module.exports = {
2727
plugins: [
2828
new ModuleFederationPlugin({
2929
remotes: {
30-
remote1: 'remote1@http://localhost:4201/remoteEntry.js',
31-
remote2: 'remote2@http://localhost:4202/remoteEntry.js',
30+
\\"remote1\\": 'remote1@http://localhost:4201/remoteEntry.js',
31+
\\"remote2\\": 'remote2@http://localhost:4202/remoteEntry.js',
3232
3333
},
3434
shared: {
@@ -72,7 +72,7 @@ module.exports = {
7272
plugins: [
7373
new ModuleFederationPlugin({
7474
remotes: {
75-
remote1: 'remote1@http://localhost:4200/remoteEntry.js',
75+
\\"remote1\\": 'remote1@http://localhost:4200/remoteEntry.js',
7676
7777
},
7878
shared: {

packages/angular/src/generators/setup-mfe/__snapshots__/setup-mfe.spec.ts.snap

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,33 @@
11
// Jest Snapshot v1, https://goo.gl/fbAQLP
22

3+
exports[`Init MFE should add a remote application and add it to a specified host applications router config 1`] = `
4+
"import { NgModule } from '@angular/core';
5+
import { BrowserModule } from '@angular/platform-browser';
6+
7+
import { AppComponent } from './app.component';
8+
import { RouterModule } from '@angular/router';
9+
10+
@NgModule({
11+
declarations: [
12+
AppComponent
13+
],
14+
imports: [
15+
BrowserModule,
16+
RouterModule.forRoot([{
17+
path: 'remote1',
18+
loadChildren: () => import('remote1/Module').then(m => m.RemoteEntryModule)
19+
}, {
20+
path: 'remote2',
21+
loadChildren: () => import('remote2/Module').then(m => m.RemoteEntryModule)
22+
}], {initialNavigation: 'enabledBlocking'})
23+
],
24+
providers: [],
25+
bootstrap: [AppComponent]
26+
})
27+
export class AppModule { }
28+
"
29+
`;
30+
331
exports[`Init MFE should add a remote application and add it to a specified host applications webpack config that contains a remote application already 1`] = `
432
"const ModuleFederationPlugin = require(\\"webpack/lib/container/ModuleFederationPlugin\\");
533
const mf = require(\\"@angular-architects/module-federation/webpack\\");
@@ -27,8 +55,8 @@ module.exports = {
2755
plugins: [
2856
new ModuleFederationPlugin({
2957
remotes: {
30-
remote1: 'remote1@http://localhost:4201/remoteEntry.js',
31-
remote2: 'remote2@http://localhost:4202/remoteEntry.js',
58+
\\"remote1\\": 'remote1@http://localhost:4201/remoteEntry.js',
59+
\\"remote2\\": 'remote2@http://localhost:4202/remoteEntry.js',
3260
3361
},
3462
shared: {
@@ -72,7 +100,7 @@ module.exports = {
72100
plugins: [
73101
new ModuleFederationPlugin({
74102
remotes: {
75-
remote1: 'remote1@http://localhost:4200/remoteEntry.js',
103+
\\"remote1\\": 'remote1@http://localhost:4200/remoteEntry.js',
76104
77105
},
78106
shared: {

packages/angular/src/generators/setup-mfe/lib/add-remote-to-host.ts

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ import type { Schema } from '../schema';
44
import { readProjectConfiguration, joinPathFragments } from '@nrwl/devkit';
55
import { tsquery } from '@phenomnomnominal/tsquery';
66
import { ObjectLiteralExpression } from 'typescript';
7+
import { addRoute } from '../../../utils/nx-devkit/ast-utils';
8+
9+
import * as ts from 'typescript';
710

811
export function addRemoteToHost(host: Tree, options: Schema) {
912
if (options.mfeType === 'remote' && options.host) {
@@ -28,7 +31,7 @@ export function addRemoteToHost(host: Tree, options: Schema) {
2831
const endOfPropertiesPos = mfRemotesNode.properties.end;
2932

3033
const updatedConfig = `${hostWebpackConfig.slice(0, endOfPropertiesPos)}
31-
\t\t${options.appName}: '${options.appName}@http://localhost:${
34+
\t\t"${options.appName}": '${options.appName}@http://localhost:${
3235
options.port ?? 4200
3336
}/remoteEntry.js',${hostWebpackConfig.slice(endOfPropertiesPos)}`;
3437

@@ -44,5 +47,38 @@ export function addRemoteToHost(host: Tree, options: Schema) {
4447
? host.read(declarationFilePath, 'utf-8')
4548
: '') + `\ndeclare module '${options.appName}/Module';`;
4649
host.write(declarationFilePath, declarationFileContent);
50+
51+
addLazyLoadedRouteToHostAppModule(host, options);
52+
}
53+
}
54+
55+
// TODO(colum): future work: allow dev to pass to path to routing module
56+
function addLazyLoadedRouteToHostAppModule(host: Tree, options: Schema) {
57+
const hostAppConfig = readProjectConfiguration(host, options.host);
58+
const pathToHostAppModule = `${hostAppConfig.sourceRoot}/app/app.module.ts`;
59+
if (!host.exists(pathToHostAppModule)) {
60+
return;
4761
}
62+
63+
const hostAppModule = host.read(pathToHostAppModule, 'utf-8');
64+
if (!hostAppModule.includes('RouterModule.forRoot(')) {
65+
return;
66+
}
67+
68+
let sourceFile = ts.createSourceFile(
69+
pathToHostAppModule,
70+
hostAppModule,
71+
ts.ScriptTarget.Latest,
72+
true
73+
);
74+
75+
sourceFile = addRoute(
76+
host,
77+
pathToHostAppModule,
78+
sourceFile,
79+
`{
80+
path: '${options.appName}',
81+
loadChildren: () => import('${options.appName}/Module').then(m => m.RemoteEntryModule)
82+
}`
83+
);
4884
}

packages/angular/src/generators/setup-mfe/lib/setup-serve-target.ts

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,43 @@ export function setupServeTarget(host: Tree, options: Schema) {
1212
appConfig.targets['serve'] = {
1313
...appConfig.targets['serve'],
1414
executor: '@nrwl/angular:webpack-server',
15+
options: {
16+
...appConfig.targets['serve'].options,
17+
port: options.port ?? undefined,
18+
},
1519
};
1620

1721
if (options.mfeType === 'host') {
18-
appConfig.targets['mfe-serve'] = {
22+
const remoteServeCommands = options.remotes
23+
? options.remotes.map((r) => `nx serve ${r}`)
24+
: undefined;
25+
const commands = remoteServeCommands
26+
? [...remoteServeCommands, `nx serve ${options.appName}`]
27+
: [`nx serve ${options.appName}`];
28+
29+
appConfig.targets['serve-mfe'] = {
1930
executor: '@nrwl/workspace:run-commands',
2031
options: {
21-
commands: [`nx serve ${options.appName}"`],
32+
commands,
2233
},
2334
};
2435
}
2536
updateProjectConfiguration(host, options.appName, appConfig);
37+
38+
if (options.mfeType === 'remote' && options.host) {
39+
const hostAppConfig = readProjectConfiguration(host, options.host);
40+
41+
hostAppConfig.targets['serve-mfe'] = {
42+
...hostAppConfig.targets['serve-mfe'],
43+
options: {
44+
...hostAppConfig.targets['serve-mfe'].options,
45+
commands: [
46+
`nx serve ${options.appName}`,
47+
...hostAppConfig.targets['serve-mfe'].options.commands,
48+
],
49+
},
50+
};
51+
52+
updateProjectConfiguration(host, options.host, hostAppConfig);
53+
}
2654
}

packages/angular/src/generators/setup-mfe/setup-mfe.spec.ts

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,11 @@ describe('Init MFE', () => {
1111
host = createTreeWithEmptyWorkspace();
1212
await applicationGenerator(host, {
1313
name: 'app1',
14+
routing: true,
1415
});
1516
await applicationGenerator(host, {
1617
name: 'remote1',
18+
routing: true,
1719
});
1820
});
1921

@@ -215,4 +217,78 @@ describe('Init MFE', () => {
215217
const hostWebpackConfig = host.read('apps/app1/webpack.config.js', 'utf-8');
216218
expect(hostWebpackConfig).toMatchSnapshot();
217219
});
220+
221+
it('should add a remote application and add it to a specified host applications router config', async () => {
222+
// ARRANGE
223+
await applicationGenerator(host, {
224+
name: 'remote2',
225+
routing: true,
226+
});
227+
228+
await setupMfe(host, {
229+
appName: 'app1',
230+
mfeType: 'host',
231+
routing: true,
232+
});
233+
234+
await setupMfe(host, {
235+
appName: 'remote1',
236+
mfeType: 'remote',
237+
host: 'app1',
238+
port: 4201,
239+
routing: true,
240+
});
241+
242+
// ACT
243+
await setupMfe(host, {
244+
appName: 'remote2',
245+
mfeType: 'remote',
246+
host: 'app1',
247+
port: 4202,
248+
routing: true,
249+
});
250+
251+
// ASSERT
252+
const hostAppModule = host.read('apps/app1/src/app/app.module.ts', 'utf-8');
253+
expect(hostAppModule).toMatchSnapshot();
254+
});
255+
256+
it('should add a remote application and add it to a specified host applications serve-mfe target', async () => {
257+
// ARRANGE
258+
await applicationGenerator(host, {
259+
name: 'remote2',
260+
routing: true,
261+
});
262+
263+
await setupMfe(host, {
264+
appName: 'app1',
265+
mfeType: 'host',
266+
routing: true,
267+
});
268+
269+
await setupMfe(host, {
270+
appName: 'remote1',
271+
mfeType: 'remote',
272+
host: 'app1',
273+
port: 4201,
274+
routing: true,
275+
});
276+
277+
// ACT
278+
await setupMfe(host, {
279+
appName: 'remote2',
280+
mfeType: 'remote',
281+
host: 'app1',
282+
port: 4202,
283+
routing: true,
284+
});
285+
286+
// ASSERT
287+
const hostAppConfig = readProjectConfiguration(host, 'app1');
288+
const serveMfe = hostAppConfig.targets['serve-mfe'];
289+
290+
expect(serveMfe.options.commands).toContain('nx serve remote1');
291+
expect(serveMfe.options.commands).toContain('nx serve remote2');
292+
expect(serveMfe.options.commands).toContain('nx serve app1');
293+
});
218294
});

0 commit comments

Comments
 (0)