feat(angular): replace mfe references with mf (#10957)

This commit is contained in:
Colum Ferry 2022-07-01 14:48:38 +01:00 committed by GitHub
parent 778f13fdaf
commit 37072bc5d6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
58 changed files with 592 additions and 212 deletions

View File

@ -196,13 +196,13 @@
"description": "Split the project configuration into `<projectRoot>/project.json` rather than including it inside `workspace.json`.",
"type": "boolean"
},
"mfe": {
"mf": {
"description": "Generate a Module Federation configuration for the application",
"type": "boolean",
"default": false,
"x-deprecated": "Use the `host` or `remote` generators instead. Support for generating Module Federation applications using the application generator will be removed in an upcoming version."
},
"mfeType": {
"mfType": {
"type": "string",
"enum": ["host", "remote"],
"description": "Type of application to generate the Module Federation configuration for.",
@ -1732,11 +1732,11 @@
"path": "/packages/angular/src/generators/scam-pipe/schema.json"
},
{
"name": "setup-mfe",
"factory": "./src/generators/setup-mfe/setup-mfe",
"name": "setup-mf",
"factory": "./src/generators/setup-mf/setup-mf",
"schema": {
"$schema": "http://json-schema.org/schema",
"$id": "GeneratorAngularMFESetup",
"$id": "GeneratorAngularMFSetup",
"cli": "nx",
"title": "Generate Module Federation Setup for Angular App",
"description": "Create Module Federation configuration files for given Angular Application.",
@ -1748,7 +1748,7 @@
"$default": { "$source": "argv", "index": 0 },
"x-prompt": "What app would you like to generate a Module Federation configuration for?"
},
"mfeType": {
"mfType": {
"type": "string",
"enum": ["host", "remote"],
"description": "Type of application to generate the Module Federation configuration for.",
@ -1794,15 +1794,15 @@
"description": "The prefix to use for any generated component."
}
},
"required": ["appName", "mfeType"],
"required": ["appName", "mfType"],
"additionalProperties": false,
"presets": []
},
"description": "Generate a Module Federation configuration for a given Angular application.",
"implementation": "/packages/angular/src/generators/setup-mfe/setup-mfe.ts",
"implementation": "/packages/angular/src/generators/setup-mf/setup-mf.ts",
"aliases": [],
"hidden": false,
"path": "/packages/angular/src/generators/setup-mfe/schema.json"
"path": "/packages/angular/src/generators/setup-mf/schema.json"
},
{
"name": "setup-tailwind",

View File

@ -39,7 +39,7 @@
"scam",
"scam-directive",
"scam-pipe",
"setup-mfe",
"setup-mf",
"setup-tailwind",
"stories",
"storybook-configuration",

View File

@ -34,12 +34,12 @@ To start with, we need to create a new Nx Workspace. We can do this easily with:
```bash
# Npm
npx create-nx-workspace ng-mfe
npx create-nx-workspace ng-mf
```
```bash
# Yarn
yarn create nx-workspace ng-mfe --packageManager=yarn
yarn create nx-workspace ng-mf --packageManager=yarn
```
You'll be prompted for a preset. We recommend selecting `empty` as it will allow you finer control over your workspace configuration.
@ -232,7 +232,7 @@ Next we want to set up our `entry.component.ts` file so that it renders a login
import { Component } from '@angular/core';
import { UserService } from '@ng-mfe/shared/data-access-user';
@Component({
selector: 'ng-mfe-login-entry',
selector: 'ng-mf-login-entry',
template: `
<div class="login-app">
<form class="login-form" (ngSubmit)="login()">
@ -330,7 +330,7 @@ import { Router } from '@angular/router';
import { distinctUntilChanged } from 'rxjs/operators';
import { UserService } from '@ng-mfe/shared/data-access-user';
@Component({
selector: 'ng-mfe-root',
selector: 'ng-mf-root',
template: `
<div class="dashboard-nav">Admin Dashboard</div>
<div *ngIf="isLoggedIn$ | async; else signIn">
@ -399,7 +399,7 @@ Well start by creating this file. Add a `module-federation.manifest.json` fil
Next, open `main.ts` under the `src/`folder and replace it with the following:
```typescript
import { setRemoteDefinitions } from '@nrwl/angular/mfe';
import { setRemoteDefinitions } from '@nrwl/angular/mf';
fetch('/assets/module-federation.manifest.json')
.then((res) => res.json())
@ -453,7 +453,7 @@ Replace it with the following:
You will also need to add the following import to the top of the file:
```typescript
import { loadRemoteModule } from '@nrwl/angular/mfe';
import { loadRemoteModule } from '@nrwl/angular/mf';
```
The `loadRemoteModule` helper method simply hides some logic that will check if the Remote application has been loaded, and if not, load it, and then requests the correct exposed module from it.

View File

@ -1,18 +1,18 @@
import {
checkFilesExist,
cleanupProject,
getSize,
killPorts,
newProject,
cleanupProject,
promisifiedTreeKill,
readFile,
runCLI,
runCommandUntil,
runCypressTests,
tmpProjPath,
uniq,
updateFile,
runCypressTests,
updateProjectConfig,
readFile,
runCommandUntil,
promisifiedTreeKill,
} from '@nrwl/e2e/utils';
import { ChildProcess } from 'child_process';
@ -222,7 +222,7 @@ describe('Angular Projects', () => {
expect(buildOutput).toContain('Successfully ran target build');
});
it('MFE - should serve the host and remote apps successfully, even with a shared library with a secondary entry point between them', async () => {
it('MF - should serve the host and remote apps successfully, even with a shared library with a secondary entry point between them', async () => {
// ACT + ASSERT
const port1 = 4200;
const port2 = 4206;
@ -343,7 +343,7 @@ describe('Angular Projects', () => {
}
}, 300000);
it('MFE - should build the host app successfully', async () => {
it('MF - should build the host app successfully', async () => {
// ARRANGE
const hostApp = uniq('app');
const remoteApp1 = uniq('remote');

View File

@ -77,8 +77,8 @@ describe('nx-dev: Packages Section', () => {
path: '/packages/angular/generators/scam-pipe',
},
{
title: '@nrwl/angular:setup-mfe',
path: '/packages/angular/generators/setup-mfe',
title: '@nrwl/angular:setup-mf',
path: '/packages/angular/generators/setup-mf',
},
{
title: '@nrwl/angular:setup-tailwind',

View File

@ -33,14 +33,20 @@ const schemaUrls = {
'/angular/library': '/packages/angular/generators/library',
'/angular/library-secondary-entry-point':
'/packages/angular/generators/library-secondary-entry-point',
'/angular/mfe-host': '/packages/angular/generators/mfe-host',
'/angular/mfe-remote': '/packages/angular/generators/mfe-remote',
'/angular/mfe-host': '/packages/angular/generators/mf-host',
'/angular/mfe-remote': '/packages/angular/generators/mf-remote',
'/packages/angular/generators/mfe-host':
'/packages/angular/generators/mf-host',
'/packages/angular/generators/mfe-remote':
'/packages/angular/generators/mf-remote',
'/angular/move': '/packages/angular/generators/move',
'/angular/ngrx': '/packages/angular/generators/ngrx',
'/angular/scam': '/packages/angular/generators/scam',
'/angular/scam-directive': '/packages/angular/generators/scam-directive',
'/angular/scam-pipe': '/packages/angular/generators/scam-pipe',
'/angular/setup-mfe': '/packages/angular/generators/setup-mfe',
'/angular/setup-mfe': '/packages/angular/generators/setup-mf',
'/packages/angular/generators/setup-mfe':
'/packages/angular/generators/setup-mf',
'/angular/setup-tailwind': '/packages/angular/generators/setup-tailwind',
'/angular/stories': '/packages/angular/generators/stories',
'/angular/storybook-configuration':

View File

@ -121,9 +121,9 @@
"schema": "./src/generators/scam-pipe/schema.json",
"description": "Generate a pipe with an accompanying Single Component Angular Module (SCAM)."
},
"setup-mfe": {
"factory": "./src/generators/setup-mfe/setup-mfe.compat",
"schema": "./src/generators/setup-mfe/schema.json",
"setup-mf": {
"factory": "./src/generators/setup-mf/setup-mf.compat",
"schema": "./src/generators/setup-mf/schema.json",
"description": "Generate a Module Federation configuration for a given Angular application."
},
"setup-tailwind": {
@ -277,9 +277,9 @@
"schema": "./src/generators/scam-pipe/schema.json",
"description": "Generate a pipe with an accompanying Single Component Angular Module (SCAM)."
},
"setup-mfe": {
"factory": "./src/generators/setup-mfe/setup-mfe",
"schema": "./src/generators/setup-mfe/schema.json",
"setup-mf": {
"factory": "./src/generators/setup-mf/setup-mf",
"schema": "./src/generators/setup-mf/schema.json",
"description": "Generate a Module Federation configuration for a given Angular application."
},
"setup-tailwind": {

View File

@ -12,7 +12,7 @@ export * from './src/generators/setup-tailwind/setup-tailwind';
export * from './src/generators/stories/stories';
export * from './src/generators/storybook-configuration/storybook-configuration';
export * from './src/generators/upgrade-module/upgrade-module';
export * from './src/generators/setup-mfe/setup-mfe';
export * from './src/generators/setup-mf/setup-mf';
export * from './src/generators/host/host';
export * from './src/generators/scam/scam';
export * from './src/generators/scam-directive/scam-directive';

View File

@ -2,4 +2,4 @@ export {
setRemoteUrlResolver,
setRemoteDefinitions,
loadRemoteModule,
} from './mfe';
} from './mf';

View File

@ -89,11 +89,11 @@
"description": "A lot of changes to how MFEs operate were discovered and merged without appropriate migrations. This should cover migrating existing MFEs to the latest, using ESM and optmized production bundles.",
"factory": "./src/migrations/update-13-5-0/update-mfe-configs"
},
"add-cypress-mfe-workaround": {
"add-cypress-mf-workaround": {
"cli": "nx",
"version": "13.8.1",
"description": "Angular doesn't attach styles.js to index.html with type=module in dev mode, meaning an error is written to the console. Cypress falls over on this error. Add logic to Cypress e2e projects to ignore this error.",
"factory": "./src/migrations/update-13-8-1/add-cypress-mfe-workaround"
"factory": "./src/migrations/update-13-8-1/add-cypress-mf-workaround"
},
"migrate-karma-config": {
"cli": "nx",
@ -148,6 +148,12 @@
"version": "14.2.0-beta.6",
"description": "Update `initialNavigation: 'enabled'` to `initialNavigation: 'enabledBlocking'`.",
"factory": "./src/migrations/update-14-2-0/update-router-initial-navigation"
},
"migrate-mfe-to-mf": {
"cli": "nx",
"version": "14.5.0-beta.0",
"description": "Update any references of MFE to MF.",
"factory": "./src/migrations/update-14-5-0/migrate-mfe-to-mf"
}
},
"packageJsonUpdates": {

View File

@ -1,2 +1,2 @@
export { withModuleFederation } from '../src/utils/mfe/with-module-federation';
export * from '../src/utils/mfe/mfe-webpack';
export { withModuleFederation } from '../src/utils/mf/with-module-federation';
export * from '../src/utils/mf/mf-webpack';

View File

@ -1,24 +1,24 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`app --mfe should add a remote application and add it to a specified host applications webpack config that contains a remote application already 1`] = `
exports[`app --mf should add a remote application and add it to a specified host applications webpack config that contains a remote application already 1`] = `
"const { withModuleFederation } = require('@nrwl/angular/module-federation');
const config = require('./module-federation.config');
module.exports = withModuleFederation(config);"
`;
exports[`app --mfe should add a remote application and add it to a specified host applications webpack config when no other remote has been added to it 1`] = `
exports[`app --mf should add a remote application and add it to a specified host applications webpack config when no other remote has been added to it 1`] = `
"const { withModuleFederation } = require('@nrwl/angular/module-federation');
const config = require('./module-federation.config');
module.exports = withModuleFederation(config);"
`;
exports[`app --mfe should generate a Module Federation correctly for a each app 1`] = `
exports[`app --mf should generate a Module Federation correctly for a each app 1`] = `
"const { withModuleFederation } = require('@nrwl/angular/module-federation');
const config = require('./module-federation.config');
module.exports = withModuleFederation(config);"
`;
exports[`app --mfe should generate a Module Federation correctly for a each app 2`] = `
exports[`app --mf should generate a Module Federation correctly for a each app 2`] = `
"const { withModuleFederation } = require('@nrwl/angular/module-federation');
const config = require('./module-federation.config');
module.exports = withModuleFederation(config);"

View File

@ -906,11 +906,11 @@ describe('app', () => {
});
});
describe('--mfe', () => {
describe('--mf', () => {
test.each(['host', 'remote'])(
'should generate a Module Federation correctly for a each app',
async (type: 'host' | 'remote') => {
await generateApp(appTree, 'my-app', { mfe: true, mfeType: type });
await generateApp(appTree, 'my-app', { mf: true, mfType: type });
expect(appTree.exists(`apps/my-app/webpack.config.js`)).toBeTruthy();
expect(
@ -925,7 +925,7 @@ describe('app', () => {
test.each(['host', 'remote'])(
'should update the builder to use webpack-browser',
async (type: 'host' | 'remote') => {
await generateApp(appTree, 'my-app', { mfe: true, mfeType: type });
await generateApp(appTree, 'my-app', { mf: true, mfType: type });
const projectConfig = readProjectConfiguration(appTree, 'my-app');
@ -938,14 +938,14 @@ describe('app', () => {
it('should add a remote application and add it to a specified host applications webpack config when no other remote has been added to it', async () => {
// ARRANGE
await generateApp(appTree, 'app1', {
mfe: true,
mfeType: 'host',
mf: true,
mfType: 'host',
});
// ACT
await generateApp(appTree, 'remote1', {
mfe: true,
mfeType: 'remote',
mf: true,
mfType: 'remote',
host: 'app1',
});
@ -960,21 +960,21 @@ describe('app', () => {
it('should add a remote application and add it to a specified host applications webpack config that contains a remote application already', async () => {
// ARRANGE
await generateApp(appTree, 'app1', {
mfe: true,
mfeType: 'host',
mf: true,
mfType: 'host',
});
await generateApp(appTree, 'remote1', {
mfe: true,
mfeType: 'remote',
mf: true,
mfType: 'remote',
host: 'app1',
port: 4201,
});
// ACT
await generateApp(appTree, 'remote2', {
mfe: true,
mfeType: 'remote',
mf: true,
mfType: 'remote',
host: 'app1',
port: 4202,
});
@ -987,7 +987,7 @@ describe('app', () => {
expect(hostWebpackConfig).toMatchSnapshot();
});
it('should add a port to a non-mfe app', async () => {
it('should add a port to a non-mf app', async () => {
// ACT
await generateApp(appTree, 'app1', {
port: 4205,

View File

@ -12,7 +12,7 @@ import { setupTailwindGenerator } from '../setup-tailwind/setup-tailwind';
import {
addE2e,
addLinting,
addMfe,
addMf,
addProxyConfig,
addRouterRootConfiguration,
addUnitTestRunner,
@ -126,8 +126,8 @@ export async function applicationGenerator(
});
}
if (options.mfe) {
await addMfe(host, options);
if (options.mf) {
await addMf(host, options);
}
if (!options.skipFormat) {

View File

@ -1,12 +1,12 @@
import type { Tree } from '@nrwl/devkit';
import type { NormalizedSchema } from './normalized-schema';
import { setupMfe } from '../../setup-mfe/setup-mfe';
import { setupMf } from '../../setup-mf/setup-mf';
export async function addMfe(host: Tree, options: NormalizedSchema) {
await setupMfe(host, {
export async function addMf(host: Tree, options: NormalizedSchema) {
await setupMf(host, {
appName: options.name,
mfeType: options.mfeType,
mfType: options.mfType,
port: options.port,
remotes: options.remotes,
host: options.host,

View File

@ -1,6 +1,6 @@
export * from './add-e2e';
export * from './add-linting';
export * from './add-mfe';
export * from './add-mf';
export * from './add-protractor';
export * from './add-proxy-config';
export * from './add-unit-test-runner';

View File

@ -23,8 +23,8 @@ export interface Schema {
backendProject?: string;
strict?: boolean;
standaloneConfig?: boolean;
mfe?: boolean;
mfeType?: 'host' | 'remote';
mf?: boolean;
mfType?: 'host' | 'remote';
remotes?: string[];
port?: number;
host?: string;

View File

@ -133,13 +133,13 @@
"description": "Split the project configuration into `<projectRoot>/project.json` rather than including it inside `workspace.json`.",
"type": "boolean"
},
"mfe": {
"mf": {
"description": "Generate a Module Federation configuration for the application",
"type": "boolean",
"default": false,
"x-deprecated": "Use the `host` or `remote` generators instead. Support for generating Module Federation applications using the application generator will be removed in an upcoming version."
},
"mfeType": {
"mfType": {
"type": "string",
"enum": ["host", "remote"],
"description": "Type of application to generate the Module Federation configuration for.",

View File

@ -23,8 +23,8 @@ describe('Host App Generator', () => {
await applicationGenerator(tree, {
name: 'remote',
mfe: true,
mfeType: 'remote',
mf: true,
mfType: 'remote',
routing: true,
port: 4201,
});

View File

@ -31,8 +31,8 @@ export default async function host(tree: Tree, options: Schema) {
const installTask = await applicationGenerator(tree, {
...options,
mfe: true,
mfeType: 'host',
mf: true,
mfType: 'host',
routing: true,
remotes: remotesToIntegrate ?? [],
port: 4200,

View File

@ -1,18 +1,18 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`MFE Remote App Generator should generate a remote mfe app with a host 1`] = `
exports[`MF Remote App Generator should generate a remote mf app with a host 1`] = `
"const { withModuleFederation } = require('@nrwl/angular/module-federation');
const config = require('./module-federation.config');
module.exports = withModuleFederation(config);"
`;
exports[`MFE Remote App Generator should generate a remote mfe app with a host 2`] = `
exports[`MF Remote App Generator should generate a remote mf app with a host 2`] = `
"const { withModuleFederation } = require('@nrwl/angular/module-federation');
const config = require('./module-federation.config');
module.exports = withModuleFederation(config);"
`;
exports[`MFE Remote App Generator should generate a remote mfe app with no host 1`] = `
exports[`MF Remote App Generator should generate a remote mf app with no host 1`] = `
"const { withModuleFederation } = require('@nrwl/angular/module-federation');
const config = require('./module-federation.config');
module.exports = withModuleFederation(config);"

View File

@ -3,8 +3,8 @@ import remote from './remote';
import applicationGenerator from '../application/application';
import { readProjectConfiguration } from '@nrwl/devkit';
describe('MFE Remote App Generator', () => {
it('should generate a remote mfe app with no host', async () => {
describe('MF Remote App Generator', () => {
it('should generate a remote mf app with no host', async () => {
// ARRANGE
const tree = createTreeWithEmptyWorkspace(2);
@ -18,14 +18,14 @@ describe('MFE Remote App Generator', () => {
expect(tree.read('apps/test/webpack.config.js', 'utf-8')).toMatchSnapshot();
});
it('should generate a remote mfe app with a host', async () => {
it('should generate a remote mf app with a host', async () => {
// ARRANGE
const tree = createTreeWithEmptyWorkspace(2);
await applicationGenerator(tree, {
name: 'host',
mfe: true,
mfeType: 'host',
mf: true,
mfType: 'host',
routing: true,
});
@ -60,7 +60,7 @@ describe('MFE Remote App Generator', () => {
}
});
it('should generate a remote mfe app and automatically find the next port available', async () => {
it('should generate a remote mf app and automatically find the next port available', async () => {
// ARRANGE
const tree = createTreeWithEmptyWorkspace(2);
await remote(tree, {
@ -78,7 +78,7 @@ describe('MFE Remote App Generator', () => {
expect(project.targets.serve.options.port).toEqual(4202);
});
it('should generate a remote mfe app and automatically find the next port available even when there are no other targets', async () => {
it('should generate a remote mf app and automatically find the next port available even when there are no other targets', async () => {
// ARRANGE
const tree = createTreeWithEmptyWorkspace(2);

View File

@ -10,11 +10,11 @@ import { getMFProjects } from '../../utils/get-mf-projects';
import { normalizeProjectName } from '../utils/project';
function findNextAvailablePort(tree: Tree) {
const mfeProjects = getMFProjects(tree);
const mfProjects = getMFProjects(tree);
const ports = new Set<number>([4200]);
for (const mfeProject of mfeProjects) {
const { targets } = readProjectConfiguration(tree, mfeProject);
for (const mfProject of mfProjects) {
const { targets } = readProjectConfiguration(tree, mfProject);
const port = targets?.serve?.options?.port ?? 4200;
ports.add(port);
}
@ -34,8 +34,8 @@ export default async function remote(tree: Tree, options: Schema) {
const installTask = await applicationGenerator(tree, {
...options,
mfe: true,
mfeType: 'remote',
mf: true,
mfType: 'remote',
routing: true,
host: options.host,
port: options.port ?? findNextAvailablePort(tree),

View File

@ -1,7 +1,7 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Init MFE --federationType=dynamic should create a host with the correct configurations 1`] = `
"import { setRemoteDefinitions } from '@nrwl/angular/mfe';
exports[`Init MF --federationType=dynamic should create a host with the correct configurations 1`] = `
"import { setRemoteDefinitions } from '@nrwl/angular/mf';
fetch('/assets/module-federation.manifest.json')
.then((res) => res.json())
@ -9,7 +9,7 @@ exports[`Init MFE --federationType=dynamic should create a host with the correct
.then(() => import('./bootstrap').catch(err => console.error(err)))"
`;
exports[`Init MFE should add a remote application and add it to a specified host applications router config 1`] = `
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';
@ -39,28 +39,28 @@ export class AppModule { }
"
`;
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`] = `
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`] = `
"module.exports = {
name: 'app1',
remotes: ['remote1','remote2',]
}"
`;
exports[`Init MFE should add a remote application and add it to a specified host applications webpack config when no other remote has been added to it 1`] = `
exports[`Init MF should add a remote application and add it to a specified host applications webpack config when no other remote has been added to it 1`] = `
"module.exports = {
name: 'app1',
remotes: ['remote1',]
}"
`;
exports[`Init MFE should add a remote to dynamic host correctly 1`] = `
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 { loadRemoteModule } from '@nrwl/angular/mfe';
import { loadRemoteModule } from '@nrwl/angular/mf';
@NgModule({
declarations: [
@ -81,26 +81,26 @@ export class AppModule { }
"
`;
exports[`Init MFE should create webpack and mfe configs correctly 1`] = `
exports[`Init MF should create webpack and mf configs correctly 1`] = `
"const { withModuleFederation } = require('@nrwl/angular/module-federation');
const config = require('./module-federation.config');
module.exports = withModuleFederation(config);"
`;
exports[`Init MFE should create webpack and mfe configs correctly 2`] = `
exports[`Init MF should create webpack and mf configs correctly 2`] = `
"module.exports = {
name: 'app1',
remotes: []
}"
`;
exports[`Init MFE should create webpack and mfe configs correctly 3`] = `
exports[`Init MF should create webpack and mf configs correctly 3`] = `
"const { withModuleFederation } = require('@nrwl/angular/module-federation');
const config = require('./module-federation.config');
module.exports = withModuleFederation(config);"
`;
exports[`Init MFE should create webpack and mfe configs correctly 4`] = `
exports[`Init MF should create webpack and mf configs correctly 4`] = `
"module.exports = {
name: 'remote1',
exposes: {
@ -109,7 +109,7 @@ exports[`Init MFE should create webpack and mfe configs correctly 4`] = `
}"
`;
exports[`Init MFE should generate the remote entry component correctly when prefix is not provided 1`] = `
exports[`Init MF should generate the remote entry component correctly when prefix is not provided 1`] = `
"import { Component } from '@angular/core';
@Component({
@ -120,7 +120,7 @@ export class RemoteEntryComponent {}
"
`;
exports[`Init MFE should generate the remote entry module and component correctly 1`] = `
exports[`Init MF should generate the remote entry module and component correctly 1`] = `
"import { Component } from '@angular/core';
@Component({
@ -131,7 +131,7 @@ export class RemoteEntryComponent {}
"
`;
exports[`Init MFE should generate the remote entry module and component correctly 2`] = `
exports[`Init MF should generate the remote entry module and component correctly 2`] = `
"import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';

View File

@ -4,10 +4,10 @@ import type { Schema } from '../schema';
export function addEntryModule(
host: Tree,
{ appName, routing, mfeType, prefix }: Schema,
{ appName, routing, mfType, prefix }: Schema,
appRoot: string
) {
if (mfeType === 'remote') {
if (mfType === 'remote') {
generateFiles(
host,
joinPathFragments(__dirname, '../files/entry-module-files'),

View File

@ -13,8 +13,8 @@ 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) {
const remoteText = mfeRemoteText.replace(/\s+/g, '');
export function checkIsCommaNeeded(mfRemoteText: string) {
const remoteText = mfRemoteText.replace(/\s+/g, '');
return !remoteText.endsWith(',]')
? remoteText === '[]'
? false
@ -23,7 +23,7 @@ export function checkIsCommaNeeded(mfeRemoteText: string) {
}
export function addRemoteToHost(tree: Tree, options: Schema) {
if (options.mfeType === 'remote' && options.host) {
if (options.mfType === 'remote' && options.host) {
const hostProject = readProjectConfiguration(tree, options.host);
const pathToMFManifest = joinPathFragments(
hostProject.sourceRoot,
@ -57,9 +57,9 @@ export function addRemoteToHost(tree: Tree, options: Schema) {
function determineHostFederationType(
tree: Tree,
pathToMfeManifest: string
pathToMfManifest: string
): 'dynamic' | 'static' {
return tree.exists(pathToMfeManifest) ? 'dynamic' : 'static';
return tree.exists(pathToMfManifest) ? 'dynamic' : 'static';
}
function addRemoteToStaticHost(
@ -99,9 +99,9 @@ function addRemoteToStaticHost(
function addRemoteToDynamicHost(
tree: Tree,
options: Schema,
pathToMfeManifest: string
pathToMfManifest: string
) {
updateJson(tree, pathToMfeManifest, (manifest) => {
updateJson(tree, pathToMfManifest, (manifest) => {
return {
...manifest,
[options.appName]: `http://localhost:${options.port}`,
@ -139,7 +139,7 @@ function addLazyLoadedRouteToHostAppModule(
sourceFile,
pathToHostAppModule,
'loadRemoteModule',
'@nrwl/angular/mfe'
'@nrwl/angular/mf'
);
}
const routeToAdd =

View File

@ -1,7 +1,6 @@
import type { Tree } from '@nrwl/devkit';
import type { Schema } from '../schema';
import { joinPathFragments } from '@nrwl/devkit';
import type { Schema } from '../schema';
export function fixBootstrap(tree: Tree, appRoot: string, options: Schema) {
const mainFilePath = joinPathFragments(appRoot, 'src/main.ts');
@ -10,7 +9,7 @@ export function fixBootstrap(tree: Tree, appRoot: string, options: Schema) {
const bootstrapImportCode = `import('./bootstrap').catch(err => console.error(err))`;
const fetchMFManifestCode = `import { setRemoteDefinitions } from '@nrwl/angular/mfe';
const fetchMFManifestCode = `import { setRemoteDefinitions } from '@nrwl/angular/mf';
fetch('/assets/module-federation.manifest.json')
.then((res) => res.json())
@ -19,7 +18,7 @@ export function fixBootstrap(tree: Tree, appRoot: string, options: Schema) {
tree.write(
mainFilePath,
options.mfeType === 'host' && options.federationType === 'dynamic'
options.mfType === 'host' && options.federationType === 'dynamic'
? fetchMFManifestCode
: bootstrapImportCode
);

View File

@ -25,7 +25,7 @@ export function generateWebpackConfig(
appRoot,
{
tmpl: '',
type: options.mfeType,
type: options.mfType,
name: options.appName,
remotes: remotesWithPorts ?? [],
projectRoot: appRoot,

View File

@ -1,13 +1,12 @@
import type { Tree } from '@nrwl/devkit';
import type { Schema } from '../schema';
import { readProjectConfiguration } from '@nrwl/devkit';
import type { Schema } from '../schema';
export function getRemotesWithPorts(host: Tree, options: Schema) {
// If type is host and remotes supplied, check remotes exist
const remotesWithPort: { remoteName: string; port: number }[] = [];
if (
options.mfeType === 'host' &&
options.mfType === 'host' &&
Array.isArray(options.remotes) &&
options.remotes.length > 0
) {

View File

@ -1,10 +1,9 @@
import type { Tree } from '@nrwl/devkit';
import { joinPathFragments, readProjectConfiguration } from '@nrwl/devkit';
import type { Schema } from '../schema';
import { readProjectConfiguration, joinPathFragments } from '@nrwl/devkit';
export function setupHostIfDynamic(tree: Tree, options: Schema) {
if (options.federationType === 'static' || options.mfeType === 'remote') {
if (options.federationType === 'static' || options.mfType === 'remote') {
return;
}

View File

@ -1,10 +1,9 @@
import type { Tree } from '@nrwl/devkit';
import type { Schema } from '../schema';
import {
readProjectConfiguration,
updateProjectConfiguration,
} from '@nrwl/devkit';
import type { Schema } from '../schema';
export function setupServeTarget(host: Tree, options: Schema) {
const appConfig = readProjectConfiguration(host, options.appName);
@ -12,7 +11,7 @@ export function setupServeTarget(host: Tree, options: Schema) {
appConfig.targets['serve'] = {
...appConfig.targets['serve'],
executor:
options.mfeType === 'host'
options.mfType === 'host'
? '@nrwl/angular:module-federation-dev-server'
: '@nrwl/angular:webpack-server',
options: {
@ -22,7 +21,7 @@ export function setupServeTarget(host: Tree, options: Schema) {
},
};
if (options.mfeType === 'remote') {
if (options.mfType === 'remote') {
appConfig.targets['serve-static'] = {
executor: '@nrwl/angular:file-server',
defaultConfiguration: 'development',

View File

@ -1,6 +1,6 @@
export interface Schema {
appName: string;
mfeType: 'host' | 'remote';
mfType: 'host' | 'remote';
port?: number;
remotes?: string[];
host?: string;

View File

@ -1,6 +1,6 @@
{
"$schema": "http://json-schema.org/schema",
"$id": "GeneratorAngularMFESetup",
"$id": "GeneratorAngularMFSetup",
"cli": "nx",
"title": "Generate Module Federation Setup for Angular App",
"description": "Create Module Federation configuration files for given Angular Application.",
@ -15,7 +15,7 @@
},
"x-prompt": "What app would you like to generate a Module Federation configuration for?"
},
"mfeType": {
"mfType": {
"type": "string",
"enum": ["host", "remote"],
"description": "Type of application to generate the Module Federation configuration for.",
@ -61,6 +61,6 @@
"description": "The prefix to use for any generated component."
}
},
"required": ["appName", "mfeType"],
"required": ["appName", "mfType"],
"additionalProperties": false
}

View File

@ -0,0 +1,4 @@
import { convertNxGenerator } from '@nrwl/devkit';
import { setupMf } from './setup-mf';
export default convertNxGenerator(setupMf);

View File

@ -1,10 +1,10 @@
import { readJson, Tree } from '@nrwl/devkit';
import { readProjectConfiguration } from '@nrwl/devkit';
import { readJson, readProjectConfiguration, Tree } from '@nrwl/devkit';
import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing';
import { setupMfe } from './setup-mfe';
import { setupMf } from './setup-mf';
import applicationGenerator from '../application/application';
describe('Init MFE', () => {
describe('Init MF', () => {
let tree: Tree;
beforeEach(async () => {
@ -23,12 +23,12 @@ describe('Init MFE', () => {
['app1', 'host'],
['remote1', 'remote'],
])(
'should create webpack and mfe configs correctly',
'should create webpack and mf configs correctly',
async (app, type: 'host' | 'remote') => {
// ACT
await setupMfe(tree, {
await setupMf(tree, {
appName: app,
mfeType: type,
mfType: type,
});
// ASSERT
@ -44,11 +44,11 @@ describe('Init MFE', () => {
);
expect(webpackContents).toMatchSnapshot();
const mfeConfigContents = tree.read(
const mfConfigContents = tree.read(
`apps/${app}/module-federation.config.js`,
'utf-8'
);
expect(mfeConfigContents).toMatchSnapshot();
expect(mfConfigContents).toMatchSnapshot();
}
);
@ -62,9 +62,9 @@ describe('Init MFE', () => {
const mainContents = tree.read(`apps/${app}/src/main.ts`, 'utf-8');
// ACT
await setupMfe(tree, {
await setupMf(tree, {
appName: app,
mfeType: type,
mfType: type,
});
// ASSERT
@ -89,9 +89,9 @@ describe('Init MFE', () => {
const mainContents = tree.read(`apps/${app}/src/main.ts`, 'utf-8');
// ACT
await setupMfe(tree, {
await setupMf(tree, {
appName: app,
mfeType: type,
mfType: type,
});
// ASSERT
@ -111,9 +111,9 @@ describe('Init MFE', () => {
'should change the build and serve target and set correct path to webpack config',
async (app, type: 'host' | 'remote') => {
// ACT
await setupMfe(tree, {
await setupMf(tree, {
appName: app,
mfeType: type,
mfType: type,
});
// ASSERT
@ -133,9 +133,9 @@ describe('Init MFE', () => {
it('should generate the remote entry module and component correctly', async () => {
// ACT
await setupMfe(tree, {
await setupMf(tree, {
appName: 'remote1',
mfeType: 'remote',
mfType: 'remote',
prefix: 'my-org',
});
@ -150,7 +150,7 @@ describe('Init MFE', () => {
it('should generate the remote entry component correctly when prefix is not provided', async () => {
// ACT
await setupMfe(tree, { appName: 'remote1', mfeType: 'remote' });
await setupMf(tree, { appName: 'remote1', mfType: 'remote' });
// ASSERT
expect(
@ -160,41 +160,41 @@ describe('Init MFE', () => {
it('should add the remote config to the host when --remotes flag supplied', async () => {
// ACT
await setupMfe(tree, {
await setupMf(tree, {
appName: 'app1',
mfeType: 'host',
mfType: 'host',
remotes: ['remote1'],
});
// ASSERT
const mfeConfigContents = tree.read(
const mfConfigContents = tree.read(
`apps/app1/module-federation.config.js`,
'utf-8'
);
expect(mfeConfigContents).toContain(`'remote1'`);
expect(mfConfigContents).toContain(`'remote1'`);
});
it('should add a remote application and add it to a specified host applications webpack config when no other remote has been added to it', async () => {
// ARRANGE
await setupMfe(tree, {
await setupMf(tree, {
appName: 'app1',
mfeType: 'host',
mfType: 'host',
});
// ACT
await setupMfe(tree, {
await setupMf(tree, {
appName: 'remote1',
mfeType: 'remote',
mfType: 'remote',
host: 'app1',
});
// ASSERT
const hostMfeConfig = tree.read(
const hostMfConfig = tree.read(
'apps/app1/module-federation.config.js',
'utf-8'
);
expect(hostMfeConfig).toMatchSnapshot();
expect(hostMfConfig).toMatchSnapshot();
});
it('should add a remote application and add it to a specified host applications webpack config that contains a remote application already', async () => {
@ -203,32 +203,32 @@ describe('Init MFE', () => {
name: 'remote2',
});
await setupMfe(tree, {
await setupMf(tree, {
appName: 'app1',
mfeType: 'host',
mfType: 'host',
});
await setupMfe(tree, {
await setupMf(tree, {
appName: 'remote1',
mfeType: 'remote',
mfType: 'remote',
host: 'app1',
port: 4201,
});
// ACT
await setupMfe(tree, {
await setupMf(tree, {
appName: 'remote2',
mfeType: 'remote',
mfType: 'remote',
host: 'app1',
port: 4202,
});
// ASSERT
const hostMfeConfig = tree.read(
const hostMfConfig = tree.read(
'apps/app1/module-federation.config.js',
'utf-8'
);
expect(hostMfeConfig).toMatchSnapshot();
expect(hostMfConfig).toMatchSnapshot();
});
it('should add a remote application and add it to a specified host applications router config', async () => {
@ -238,24 +238,24 @@ describe('Init MFE', () => {
routing: true,
});
await setupMfe(tree, {
await setupMf(tree, {
appName: 'app1',
mfeType: 'host',
mfType: 'host',
routing: true,
});
await setupMfe(tree, {
await setupMf(tree, {
appName: 'remote1',
mfeType: 'remote',
mfType: 'remote',
host: 'app1',
port: 4201,
routing: true,
});
// ACT
await setupMfe(tree, {
await setupMf(tree, {
appName: 'remote2',
mfeType: 'remote',
mfType: 'remote',
host: 'app1',
port: 4202,
routing: true,
@ -274,9 +274,9 @@ describe('Init MFE', () => {
});
// ACT
await setupMfe(tree, {
await setupMf(tree, {
appName: 'test-app',
mfeType: 'host',
mfType: 'host',
routing: true,
});
@ -293,9 +293,9 @@ describe('Init MFE', () => {
describe('--federationType=dynamic', () => {
it('should create a host with the correct configurations', async () => {
// ARRANGE & ACT
await setupMfe(tree, {
await setupMf(tree, {
appName: 'app1',
mfeType: 'host',
mfType: 'host',
routing: true,
federationType: 'dynamic',
});
@ -313,17 +313,17 @@ describe('Init MFE', () => {
it('should add a remote to dynamic host correctly', async () => {
// ARRANGE
await setupMfe(tree, {
await setupMf(tree, {
appName: 'app1',
mfeType: 'host',
mfType: 'host',
routing: true,
federationType: 'dynamic',
});
// ACT
await setupMfe(tree, {
await setupMf(tree, {
appName: 'remote1',
mfeType: 'remote',
mfType: 'remote',
port: 4201,
host: 'app1',
routing: true,

View File

@ -1,8 +1,7 @@
import type { Tree } from '@nrwl/devkit';
import { formatFiles, readProjectConfiguration } from '@nrwl/devkit';
import type { Schema } from './schema';
import { readProjectConfiguration, formatFiles } from '@nrwl/devkit';
import {
addCypressOnErrorWorkaround,
addEntryModule,
@ -11,12 +10,12 @@ import {
fixBootstrap,
generateWebpackConfig,
getRemotesWithPorts,
setupServeTarget,
setupHostIfDynamic,
setupServeTarget,
updateTsConfigTarget,
} from './lib';
export async function setupMfe(tree: Tree, options: Schema) {
export async function setupMf(tree: Tree, options: Schema) {
const projectConfig = readProjectConfiguration(tree, options.appName);
options.federationType = options.federationType ?? 'static';
@ -43,4 +42,4 @@ export async function setupMfe(tree: Tree, options: Schema) {
}
}
export default setupMfe;
export default setupMf;

View File

@ -1,4 +0,0 @@
import { convertNxGenerator } from '@nrwl/devkit';
import { setupMfe } from './setup-mfe';
export default convertNxGenerator(setupMfe);

View File

@ -1,8 +1,8 @@
import { removeProjectConfiguration, Tree } from '@nrwl/devkit';
import { removeProjectConfiguration } from '@nrwl/devkit';
import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing';
import applicationGenerator from '../../generators/application/application';
import setupMfe from '../../generators/setup-mfe/setup-mfe';
import addCypressMfeWorkaround from './add-cypress-mfe-workaround';
import setupMf from '../../generators/setup-mf/setup-mf';
import addCypressMfWorkaround from './add-cypress-mf-workaround';
describe('Add Cypress MFE Workaround', () => {
it('should add the cypress command to the index.ts for project that has associated e2e', async () => {
@ -13,12 +13,12 @@ describe('Add Cypress MFE Workaround', () => {
routing: true,
});
await setupMfe(tree, { appName: 'app1', mfeType: 'host', routing: true });
await setupMf(tree, { appName: 'app1', mfType: 'host', routing: true });
tree.write('apps/app1-e2e/src/support/index.ts', '');
// ACT
await addCypressMfeWorkaround(tree);
await addCypressMfWorkaround(tree);
// ASSERT
expect(tree.read('apps/app1-e2e/src/support/index.ts', 'utf-8')).toContain(
@ -34,13 +34,13 @@ describe('Add Cypress MFE Workaround', () => {
routing: true,
});
await setupMfe(tree, { appName: 'app1', mfeType: 'host', routing: true });
await setupMf(tree, { appName: 'app1', mfType: 'host', routing: true });
removeProjectConfiguration(tree, 'app1-e2e');
tree.delete('apps/app1-e2e');
// ACT
await addCypressMfeWorkaround(tree);
await addCypressMfWorkaround(tree);
// ASSERT
expect(tree.exists('apps/app1-e2e/src/support/index.ts')).toBeFalsy();

View File

@ -0,0 +1,229 @@
import type { Tree } from '@nrwl/devkit';
import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing';
import migrateMfeToMf, {
renameSetupMfeGeneratorUsages,
replaceExportedMFETypes,
replaceNrwlAngularMfImport,
} from './migrate-mfe-to-mf';
describe('migrate-mfe-to-mf', () => {
let tree: Tree;
beforeEach(() => {
tree = createTreeWithEmptyWorkspace(2);
});
it('should replace any imports from nrwl/angular/mfe', () => {
// ARRANGE
const file = `import { loadRemoteModule } from '@nrwl/angular/mfe';
// But not comments, or other markdown etc @nrwl/angular/mfe
function something() {
// but this should change
import('@nrwl/angular/mfe');
}
`;
// ACT
const updatedFile = replaceNrwlAngularMfImport(file);
// ASSERT
expect(updatedFile).toMatchInlineSnapshot(`
"import { loadRemoteModule } from '@nrwl/angular/mf';
// But not comments, or other markdown etc @nrwl/angular/mfe
function something() {
// but this should change
import('@nrwl/angular/mf');
}
"
`);
});
it('should replace type imports from nrwl/angular/module-federation', () => {
// ARRANGE
const file = `import { MFERemotes } from '@nrwl/angular/module-federation';
import { MFEConfig } from '@nrwl/angular/module-federation';
const myValue: MFEConfig = {};
const myRemotes: MFERemotes = [];
function doSomething(v: MFERemotes): MFEConfig {};
`;
// ACT
const updatedFile = replaceExportedMFETypes(file);
// ASSERT
expect(updatedFile).toMatchInlineSnapshot(`
"import { MFRemotes } from '@nrwl/angular/module-federation';
import { MFConfig } from '@nrwl/angular/module-federation';
const myValue: MFConfig = {};
const myRemotes: MFRemotes = [];
function doSomething(v: MFRemotes): MFConfig {};
"
`);
});
it('should rename usages of setupMfe', () => {
// ARRANGE
const file = `import { setupMfe } from '@nrwl/angular/generators';
import { setupMfe, somethingElse } from '@nrwl/angular/generators';
function doSomething(v: MFERemotes): MFEConfig {
setupMfe();
setupMfe({
mfeType: 'doSomething'
})
};
`;
// ACT
const updatedFile = renameSetupMfeGeneratorUsages(file);
// ASSERT
expect(updatedFile).toMatchInlineSnapshot(`
"import { setupMf } from '@nrwl/angular/generators';
import { setupMf, somethingElse } from '@nrwl/angular/generators';
function doSomething(v: MFERemotes): MFEConfig {
setupMf();
setupMf({
mfType: 'doSomething'
})
};
"
`);
});
it('should update files correctly', async () => {
// ARRANGE
tree.write(
'test1.js',
`import { loadRemoteModule } from '@nrwl/angular/mfe';
// But not comments, or other markdown etc @nrwl/angular/mfe
function something() {
// but this should change
import('@nrwl/angular/mfe');
}`
);
tree.write(
'test2.ts',
`import { MFERemotes } from '@nrwl/angular/module-federation';
import { MFEConfig } from '@nrwl/angular/module-federation';
const myValue: MFEConfig = {};
const myRemotes: MFERemotes = [];
function doSomething(v: MFERemotes): MFEConfig {};
`
);
tree.write(
'apps/app1/test3.ts',
`import { loadRemoteModule } from '@nrwl/angular/mfe';
import { MFERemotes, MFEConfig } from '@nrwl/angular/module-federation';
// But not comments, or other markdown etc @nrwl/angular/mfe
function something() {
// but this should change
import('@nrwl/angular/mfe');
}
const myValue: MFEConfig = {};
const myRemotes: MFERemotes = [];
function doSomething(v: MFERemotes): MFEConfig {};
`
);
tree.write(
'libs/plugins/my-plugin/src/generators/my-generator.ts',
`import { setupMfe } from '@nrwl/angular/generators';
import { setupMfe, somethingElse } from '@nrwl/angular/generators';
function doSomething(v: MFERemotes): MFEConfig {
setupMfe();
setupMfe({
mfeType: 'doSomething'
})
};
`
);
// ACT
await migrateMfeToMf(tree);
// ASSERT
expect(tree.read('test1.js', 'utf-8')).toMatchInlineSnapshot(`
"import { loadRemoteModule } from '@nrwl/angular/mf';
// But not comments, or other markdown etc @nrwl/angular/mfe
function something() {
// but this should change
import('@nrwl/angular/mf');
}"
`);
expect(tree.read('test2.ts', 'utf-8')).toMatchInlineSnapshot(`
"import { MFRemotes } from '@nrwl/angular/module-federation';
import { MFConfig } from '@nrwl/angular/module-federation';
const myValue: MFConfig = {};
const myRemotes: MFRemotes = [];
function doSomething(v: MFRemotes): MFConfig {};
"
`);
expect(tree.read('apps/app1/test3.ts', 'utf-8')).toMatchInlineSnapshot(`
"import { loadRemoteModule } from '@nrwl/angular/mf';
import { MFRemotes, MFConfig } from '@nrwl/angular/module-federation';
// But not comments, or other markdown etc @nrwl/angular/mfe
function something() {
// but this should change
import('@nrwl/angular/mf');
}
const myValue: MFConfig = {};
const myRemotes: MFRemotes = [];
function doSomething(v: MFRemotes): MFConfig {};
"
`);
expect(
tree.read(
'libs/plugins/my-plugin/src/generators/my-generator.ts',
'utf-8'
)
).toMatchInlineSnapshot(`
"import { setupMf } from '@nrwl/angular/generators';
import { setupMf, somethingElse } from '@nrwl/angular/generators';
function doSomething(v: MFERemotes): MFEConfig {
setupMf();
setupMf({
mfType: 'doSomething'
})
};
"
`);
});
});

View File

@ -0,0 +1,144 @@
import type { Tree } from '@nrwl/devkit';
import { visitNotIgnoredFiles } from '@nrwl/devkit';
import { extname } from 'path';
import { tsquery } from '@phenomnomnominal/tsquery';
import { SourceFile } from 'typescript';
const NRWL_ANGULAR_MFE_STATIC_IMPORT_SELECTOR =
'ImportDeclaration > StringLiteral[value="@nrwl/angular/mfe"]';
const NRWL_ANGULAR_MFE_DYNAMIC_IMPORT_SELECTOR =
'CallExpression:has(ImportKeyword) > StringLiteral[value="@nrwl/angular/mfe"]';
const NRWL_ANGULAR_MFE_TYPES_SELECTOR =
'ImportDeclaration:has(StringLiteral[value=@nrwl/angular/module-federation]) > ImportClause > NamedImports';
export function replaceNrwlAngularMfImport(fileContents: string) {
let fileAst: SourceFile = tsquery.ast(fileContents);
if (fileContents.includes('@nrwl/angular/mfe')) {
// This file definitely contains the string, however, we're interested in whether it is an import
const staticQueryResult = tsquery(
fileAst,
NRWL_ANGULAR_MFE_STATIC_IMPORT_SELECTOR,
{
visitAllChildren: true,
}
);
if (staticQueryResult && staticQueryResult.length > 0) {
fileContents = `${fileContents.slice(
0,
staticQueryResult[0].getStart()
)}'@nrwl/angular/mf'${fileContents.slice(staticQueryResult[0].getEnd())}`;
}
fileAst = tsquery.ast(fileContents);
const dynamicQueryResult = tsquery(
fileAst,
NRWL_ANGULAR_MFE_DYNAMIC_IMPORT_SELECTOR,
{
visitAllChildren: true,
}
);
if (dynamicQueryResult && dynamicQueryResult.length > 0) {
fileContents = `${fileContents.slice(
0,
dynamicQueryResult[0].getStart()
)}'@nrwl/angular/mf'${fileContents.slice(
dynamicQueryResult[0].getEnd()
)}`;
}
}
return fileContents;
}
export function replaceExportedMFETypes(fileContents: string) {
const ast = tsquery.ast(fileContents);
const queryResult = tsquery(ast, NRWL_ANGULAR_MFE_TYPES_SELECTOR, {
visitAllChildren: true,
});
if (queryResult && queryResult.length > 0) {
const TYPES_IMPORTED_FROM_NRWL_REGEX =
/(MFERemotes|MFEConfig)+.*from+.+(@nrwl\/angular\/module-federation)+/g;
if (TYPES_IMPORTED_FROM_NRWL_REGEX.test(fileContents)) {
fileContents = fileContents.replace(/MFERemotes/g, 'MFRemotes');
fileContents = fileContents.replace(/MFEConfig/g, 'MFConfig');
}
}
return fileContents;
}
export function renameSetupMfeGeneratorUsages(fileContents: string) {
// Attempt to update any custom generator usage of the changed generators
const NRWL_SETUP_MFE_IMPORT_SELECTOR =
'ImportDeclaration:has(StringLiteral[value=@nrwl/angular/generators]) > ImportClause:has(NamedImports:has(ImportSpecifier > Identifier[name=setupMfe]))';
const SETUP_MFE_IMPORTED_FROM_NRWL_REGEX =
/(setupMfe)+.*from+.+(@nrwl\/angular\/generators)+/g;
const SETUP_MFE_FUNCTION_CALL_MFE_TYPE_PROPERTY_ASSIGNMENT_SELECTOR =
'CallExpression:has(Identifier[name=setupMf]) ObjectLiteralExpression > PropertyAssignment > Identifier[name=mfeType]';
let ast = tsquery.ast(fileContents);
let queryResult = tsquery(ast, NRWL_SETUP_MFE_IMPORT_SELECTOR, {
visitAllChildren: true,
});
if (
queryResult &&
queryResult.length > 0 &&
SETUP_MFE_IMPORTED_FROM_NRWL_REGEX.test(fileContents)
) {
fileContents = fileContents.replace(/setupMfe/g, 'setupMf');
}
ast = tsquery.ast(fileContents);
queryResult = tsquery(
ast,
SETUP_MFE_FUNCTION_CALL_MFE_TYPE_PROPERTY_ASSIGNMENT_SELECTOR,
{
visitAllChildren: true,
}
);
while (queryResult && queryResult.length > 0) {
const node = queryResult[0];
fileContents = `${fileContents.slice(
0,
node.getStart()
)}mfType${fileContents.slice(node.getEnd())}`;
ast = tsquery.ast(fileContents);
queryResult = tsquery(
ast,
SETUP_MFE_FUNCTION_CALL_MFE_TYPE_PROPERTY_ASSIGNMENT_SELECTOR,
{
visitAllChildren: true,
}
);
}
return fileContents;
}
export default async function (tree: Tree) {
visitNotIgnoredFiles(tree, '/', (path) => {
const pathExtName = extname(path);
let fileContents = tree.read(path, 'utf-8');
if (pathExtName === '.ts' || pathExtName === '.js') {
fileContents = replaceNrwlAngularMfImport(fileContents);
}
if (pathExtName === '.ts') {
// Only TS files can import types and interfaces
fileContents = replaceExportedMFETypes(fileContents);
fileContents = renameSetupMfeGeneratorUsages(fileContents);
}
tree.write(path, fileContents);
});
}

View File

@ -4,9 +4,9 @@ import * as fs from 'fs';
import * as tsUtils from '@nrwl/workspace/src/utilities/typescript';
import * as devkit from '@nrwl/devkit';
import { sharePackages, shareWorkspaceLibraries } from './mfe-webpack';
import { sharePackages, shareWorkspaceLibraries } from './mf-webpack';
describe('MFE Webpack Utils', () => {
describe('MF Webpack Utils', () => {
afterEach(() => jest.clearAllMocks());
describe('ShareWorkspaceLibraries', () => {
@ -20,7 +20,7 @@ describe('MFE Webpack Utils', () => {
} catch (error) {
// ASSERT
expect(error.message).toContain(
'NX MFE: TsConfig Path for workspace libraries does not exist!'
'NX MF: TsConfig Path for workspace libraries does not exist!'
);
}
});
@ -79,7 +79,7 @@ describe('MFE Webpack Utils', () => {
} catch (error) {
// ASSERT
expect(error.message).toEqual(
'NX MFE: Could not find root package.json to determine dependency versions.'
'NX MF: Could not find root package.json to determine dependency versions.'
);
}
});

View File

@ -87,7 +87,7 @@ export function shareWorkspaceLibraries(
) {
if (!existsSync(tsConfigPath)) {
throw new Error(
`NX MFE: TsConfig Path for workspace libraries does not exist! (${tsConfigPath})`
`NX MF: TsConfig Path for workspace libraries does not exist! (${tsConfigPath})`
);
}

View File

@ -8,7 +8,7 @@ export function readRootPackageJson(): {
const pkgJsonPath = joinPathFragments(workspaceRoot, 'package.json');
if (!existsSync(pkgJsonPath)) {
throw new Error(
'NX MFE: Could not find root package.json to determine dependency versions.'
'NX MF: Could not find root package.json to determine dependency versions.'
);
}

View File

@ -3,7 +3,7 @@ import {
SharedLibraryConfig,
sharePackages,
shareWorkspaceLibraries,
} from './mfe-webpack';
} from './mf-webpack';
import {
createProjectGraphAsync,
ProjectGraph,
@ -16,10 +16,10 @@ import {
import { ParsedCommandLine } from 'typescript';
import { readRootPackageJson } from './utils';
import { extname, join } from 'path';
import ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');
import { readCachedProjectConfiguration } from 'nx/src/project-graph/project-graph';
import ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');
export type MFERemotes = string[] | [remoteName: string, remoteUrl: string][];
export type MFRemotes = string[] | [remoteName: string, remoteUrl: string][];
type SharedFunction = (
libraryName: string,
@ -31,9 +31,9 @@ type AdditionalSharedConfig = Array<
| { libraryName: string; sharedConfig: SharedLibraryConfig }
>;
export interface MFEConfig {
export interface MFConfig {
name: string;
remotes?: MFERemotes;
remotes?: MFRemotes;
exposes?: Record<string, string>;
shared?: SharedFunction;
additionalShared?: AdditionalSharedConfig;
@ -134,7 +134,7 @@ function determineRemoteUrl(remote: string) {
}/remoteEntry.mjs`;
}
function mapRemotes(remotes: MFERemotes) {
function mapRemotes(remotes: MFRemotes) {
const mappedRemotes = {};
for (const remote of remotes) {
@ -233,8 +233,8 @@ function applyDefaultEagerPackages(
}
}
export async function withModuleFederation(options: MFEConfig) {
const DEFAULT_NPM_PACKAGES_TO_AVOID = ['zone.js', '@nrwl/angular/mfe'];
export async function withModuleFederation(options: MFConfig) {
const DEFAULT_NPM_PACKAGES_TO_AVOID = ['zone.js', '@nrwl/angular/mf'];
let projectGraph: ProjectGraph<any>;
try {