Skip to content

Commit

Permalink
fix(angular): fix dynamic module federation generation (#22724)
Browse files Browse the repository at this point in the history
(cherry picked from commit 18a2878)
  • Loading branch information
leosvelperez authored and FrozenPandaz committed Apr 8, 2024
1 parent b9f9ac1 commit 91a2dfc
Show file tree
Hide file tree
Showing 8 changed files with 135 additions and 36 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,40 @@ fetch('/assets/module-federation.manifest.json')
.then(() => import('./bootstrap').catch(err => console.error(err)));"
`;

exports[`Init MF --federationType=dynamic should wire up existing remote to dynamic host correctly 1`] = `
"import { NxWelcomeComponent } from './nx-welcome.component';
import { Route } from '@angular/router';
import { loadRemoteModule } from '@nx/angular/mf';
export const appRoutes: Route[] = [
{
path: 'remote1',
loadChildren: () => loadRemoteModule('remote1', './Module').then(m => m.RemoteEntryModule)
},
{
path: '',
component: NxWelcomeComponent
},];
"
`;

exports[`Init MF --federationType=dynamic should wire up existing remote to dynamic host correctly when --typescriptConfiguration=true 1`] = `
"import { NxWelcomeComponent } from './nx-welcome.component';
import { Route } from '@angular/router';
import { loadRemoteModule } from '@nx/angular/mf';
export const appRoutes: Route[] = [
{
path: 'remote1',
loadChildren: () => loadRemoteModule('remote1', './Module').then(m => m.RemoteEntryModule)
},
{
path: '',
component: NxWelcomeComponent
},];
"
`;

exports[`Init MF should add a remote application and add it to a specified host applications router config 1`] = `
"import { NxWelcomeComponent } from './nx-welcome.component';
import { Route } from '@angular/router';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const config: ModuleFederationConfig = {
* declare module 'my-external-remote';
*
*/
remotes: [<% remotes.forEach(function(remote) { %>'<%= remote.remoteName %>',<% }); %>]<% } %><% if(type === 'remote') { %>
remotes: [<% if (federationType === 'static') { remotes.forEach(function(remote) { %>'<%= remote.remoteName %>',<% }); } %>]<% } %><% if(type === 'remote') { %>
exposes: {<% if(standalone) { %>
'./Routes': '<%= projectRoot %>/src/app/remote-entry/entry.routes.ts',<% } else { %>
'./Module': '<%= projectRoot %>/src/app/remote-entry/entry.module.ts',<% } %>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ module.exports = {
* declare module 'my-external-remote';
*
*/
remotes: [<% remotes.forEach(function(remote) { %>'<%= remote.remoteName %>',<% }); %>]<% } %><% if(type === 'remote') { %>
remotes: [<% if (federationType === 'static') { remotes.forEach(function(remote) { %>'<%= remote.remoteName %>',<% }); } %>]<% } %><% if(type === 'remote') { %>
exposes: {<% if(standalone) { %>
'./Routes': '<%= projectRoot %>/src/app/remote-entry/entry.routes.ts',<% } else { %>
'./Module': '<%= projectRoot %>/src/app/remote-entry/entry.module.ts',<% } %>
Expand Down
34 changes: 13 additions & 21 deletions packages/angular/src/generators/setup-mf/lib/add-remote-to-host.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,20 @@ import {
Tree,
updateJson,
} from '@nx/devkit';
import type { Schema } from '../schema';
import { ArrayLiteralExpression } from 'typescript';
import { insertImport } from '@nx/js';
import { addRoute } from '../../../utils/nx-devkit/route-utils';
import { ensureTypescript } from '@nx/js/src/utils/typescript/ensure-typescript';

let tsModule: typeof import('typescript');

export type AddRemoteOptions = {
host: string;
appName: string;
standalone: boolean;
port: number;
};

export function checkIsCommaNeeded(mfRemoteText: string) {
const remoteText = mfRemoteText.replace(/\s+/g, '');
return !remoteText.endsWith(',]')
Expand All @@ -23,7 +29,7 @@ export function checkIsCommaNeeded(mfRemoteText: string) {
: false;
}

export function addRemoteToHost(tree: Tree, options: Schema) {
export function addRemoteToHost(tree: Tree, options: AddRemoteOptions) {
if (options.host) {
const hostProject = readProjectConfiguration(tree, options.host);
const pathToMFManifest = joinPathFragments(
Expand All @@ -50,20 +56,6 @@ export function addRemoteToHost(tree: Tree, options: Schema) {
addRemoteToDynamicHost(tree, options, pathToMFManifest);
}

// const declarationFilePath = joinPathFragments(
// hostProject.sourceRoot,
// 'remotes.d.ts'
// );
//
// const declarationFileContent =
// (tree.exists(declarationFilePath)
// ? tree.read(declarationFilePath, 'utf-8')
// : '') +
// `\ndeclare module '${options.appName}/${
// options.standalone ? `Routes` : `Module`
// }';`;
// tree.write(declarationFilePath, declarationFileContent);

addLazyLoadedRouteToHostAppModule(tree, options, hostFederationType);
}
}
Expand All @@ -77,13 +69,13 @@ function determineHostFederationType(

function addRemoteToStaticHost(
tree: Tree,
options: Schema,
options: AddRemoteOptions,
hostProject: ProjectConfiguration,
isHostUsingTypescrpt: boolean
isHostUsingTypescript: boolean
) {
const hostMFConfigPath = joinPathFragments(
hostProject.root,
isHostUsingTypescrpt
isHostUsingTypescript
? 'module-federation.config.ts'
: 'module-federation.config.js'
);
Expand Down Expand Up @@ -115,7 +107,7 @@ function addRemoteToStaticHost(

function addRemoteToDynamicHost(
tree: Tree,
options: Schema,
options: AddRemoteOptions,
pathToMfManifest: string
) {
updateJson(tree, pathToMfManifest, (manifest) => {
Expand All @@ -129,7 +121,7 @@ function addRemoteToDynamicHost(
// TODO(colum): future work: allow dev to pass to path to routing module
function addLazyLoadedRouteToHostAppModule(
tree: Tree,
options: Schema,
options: AddRemoteOptions,
hostFederationType: 'dynamic' | 'static'
) {
if (!tsModule) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export function generateWebpackConfig(
{
tmpl: '',
type: options.mfType,
federationType: options.federationType,
name: options.appName,
remotes: remotesWithPorts ?? [],
projectRoot: appRoot,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,10 @@ export function updateHostAppRoutes(tree: Tree, options: Schema) {

const { sourceRoot } = readProjectConfiguration(tree, options.appName);

const remoteRoutes =
options.remotes && Array.isArray(options.remotes)
? options.remotes.reduce(
(routes, remote) =>
`${routes}\n<li><a routerLink='${remote}'>${
names(remote).className
}</a></li>`,
''
)
: '';

tree.write(
joinPathFragments(sourceRoot, 'app/app.component.html'),
`<ul class="remote-menu">
<li><a routerLink="/">Home</a></li>${remoteRoutes}
<li><a routerLink="/">Home</a></li>
</ul>
<router-outlet></router-outlet>
`
Expand Down
70 changes: 70 additions & 0 deletions packages/angular/src/generators/setup-mf/setup-mf.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -535,6 +535,76 @@ describe('Init MF', () => {
).toBeTruthy();
expect(tree.read('app1/src/main.ts', 'utf-8')).toMatchSnapshot();
});

it('should wire up existing remote to dynamic host correctly', async () => {
await setupMf(tree, {
appName: 'remote1',
mfType: 'remote',
port: 4201,
routing: true,
typescriptConfiguration: false,
standalone: false,
skipFormat: true,
});

await setupMf(tree, {
appName: 'app1',
mfType: 'host',
routing: true,
federationType: 'dynamic',
remotes: ['remote1'],
typescriptConfiguration: false,
standalone: false,
skipFormat: true,
});

expect(tree.read('app1/module-federation.config.js', 'utf-8')).toContain(
'remotes: []'
);
expect(
readJson(tree, 'app1/src/assets/module-federation.manifest.json')
).toEqual({
remote1: 'http://localhost:4201',
});
expect(
tree.read('app1/src/app/app.routes.ts', 'utf-8')
).toMatchSnapshot();
});

it('should wire up existing remote to dynamic host correctly when --typescriptConfiguration=true', async () => {
await setupMf(tree, {
appName: 'remote1',
mfType: 'remote',
port: 4201,
routing: true,
typescriptConfiguration: true,
standalone: false,
skipFormat: true,
});

await setupMf(tree, {
appName: 'app1',
mfType: 'host',
routing: true,
federationType: 'dynamic',
remotes: ['remote1'],
typescriptConfiguration: true,
standalone: false,
skipFormat: true,
});

expect(tree.read('app1/module-federation.config.ts', 'utf-8')).toContain(
'remotes: []'
);
expect(
readJson(tree, 'app1/src/assets/module-federation.manifest.json')
).toEqual({
remote1: 'http://localhost:4201',
});
expect(
tree.read('app1/src/app/app.routes.ts', 'utf-8')
).toMatchSnapshot();
});
});

it('should add a remote to dynamic host correctly', async () => {
Expand Down
15 changes: 14 additions & 1 deletion packages/angular/src/generators/setup-mf/setup-mf.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,12 @@ export async function setupMf(tree: Tree, rawOptions: Schema) {

let installTask = () => {};
if (options.mfType === 'remote') {
addRemoteToHost(tree, options);
addRemoteToHost(tree, {
appName: options.appName,
host: options.host,
standalone: options.standalone,
port: options.port,
});
addRemoteEntry(tree, options, projectConfig.root);
removeDeadCodeFromRemote(tree, options);
setupTspathForRemote(tree, options);
Expand All @@ -54,6 +59,14 @@ export async function setupMf(tree: Tree, rawOptions: Schema) {
if (options.mfType === 'host') {
setupHostIfDynamic(tree, options);
updateHostAppRoutes(tree, options);
for (const { remoteName, port } of remotesWithPorts) {
addRemoteToHost(tree, {
appName: remoteName,
host: options.appName,
standalone: options.standalone,
port,
});
}
installTask = addDependenciesToPackageJson(
tree,
{},
Expand Down

0 comments on commit 91a2dfc

Please sign in to comment.