Skip to content

Commit

Permalink
feat(angular): add mfe-host generator (#9218)
Browse files Browse the repository at this point in the history
  • Loading branch information
Coly010 committed Mar 8, 2022
1 parent 0a17a61 commit 7442030
Show file tree
Hide file tree
Showing 11 changed files with 400 additions and 1 deletion.
60 changes: 60 additions & 0 deletions docs/generated/api-angular/generators/mfe-host.md
@@ -0,0 +1,60 @@
---
title: '@nrwl/angular:mfe-host generator'
description: 'Generate a Host Angular Micro Frontend Application.'
---

# @nrwl/angular:mfe-host

Generate a Host Angular Micro Frontend Application.

## Usage

```bash
nx generate mfe-host ...
```

```bash
nx g host ... # same
```

By default, Nx will search for `mfe-host` in the default collection provisioned in `workspace.json`.

You can specify the collection explicitly as follows:

```bash
nx g @nrwl/angular:mfe-host ...
```

Show what will be generated without writing to disk:

```bash
nx g mfe-host ... --dry-run
```

### Examples

Create an Angular app with configuration in place for MFE. If remotes is provided, attach the remote app to this app's configuration.:

```bash
nx g @nrwl/angular:mfe-host appName --remotes=remote1
```

## Options

### name (_**required**_)

Type: `string`

The name to give to the host Angular app.

### host

Type: `string`

The name of the host app to attach this host app to.

### port

Type: `string`

The port on which this app should be served.
5 changes: 5 additions & 0 deletions docs/map.json
Expand Up @@ -638,6 +638,11 @@
"id": "library-secondary-entry-point",
"file": "generated/api-angular/generators/library-secondary-entry-point"
},
{
"name": "mfe-host generator",
"id": "mfe-host",
"file": "generated/api-angular/generators/mfe-host"
},
{
"name": "move generator",
"id": "move",
Expand Down
12 changes: 12 additions & 0 deletions packages/angular/generators.json
Expand Up @@ -74,6 +74,12 @@
"aliases": ["mv"],
"description": "Moves an Angular application or library to another folder within the workspace and updates the project configuration."
},
"mfe-host": {
"factory": "./src/generators/mfe-host/mfe-host.compat",
"schema": "./src/generators/mfe-host/schema.json",
"aliases": ["host"],
"description": "Generate a Host Angular Micro Frontend Application."
},
"ngrx": {
"factory": "./src/generators/ngrx/compat",
"schema": "./src/generators/ngrx/schema.json",
Expand Down Expand Up @@ -207,6 +213,12 @@
"aliases": ["mv"],
"description": "Moves an Angular application or library to another folder within the workspace and updates the project configuration."
},
"mfe-host": {
"factory": "./src/generators/mfe-host/mfe-host",
"schema": "./src/generators/mfe-host/schema.json",
"aliases": ["host"],
"description": "Generate a Host Angular Micro Frontend Application."
},
"ngrx": {
"factory": "./src/generators/ngrx/ngrx",
"schema": "./src/generators/ngrx/schema.json",
Expand Down
1 change: 1 addition & 0 deletions packages/angular/generators.ts
Expand Up @@ -15,6 +15,7 @@ export * from './src/generators/storybook-migrate-defaults-5-to-6/storybook-migr
export * from './src/generators/storybook-migrate-stories-to-6-2/migrate-stories-to-6-2';
export * from './src/generators/upgrade-module/upgrade-module';
export * from './src/generators/setup-mfe/setup-mfe';
export * from './src/generators/mfe-host/mfe-host';
export * from './src/generators/scam/scam';
export * from './src/generators/scam-directive/scam-directive';
export * from './src/generators/scam-pipe/scam-pipe';
Expand Down
@@ -0,0 +1,189 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`MFE Host App Generator should generate a host mfe app with a remote 1`] = `
"const ModuleFederationPlugin = require(\\"webpack/lib/container/ModuleFederationPlugin\\");
const mf = require(\\"@angular-architects/module-federation/webpack\\");
const path = require(\\"path\\");
const share = mf.share;
/**
* We use the NX_TSCONFIG_PATH environment variable when using the @nrwl/angular:webpack-browser
* builder as it will generate a temporary tsconfig file which contains any required remappings of
* shared libraries.
* A remapping will occur when a library is buildable, as webpack needs to know the location of the
* built files for the buildable library.
* This NX_TSCONFIG_PATH environment variable is set by the @nrwl/angular:webpack-browser and it contains
* the location of the generated temporary tsconfig file.
*/
const tsConfigPath = process.env.NX_TSCONFIG_PATH ?? path.join(__dirname, '../../tsconfig.base.json');
const workspaceRootPath = path.join(__dirname, '../../');
const sharedMappings = new mf.SharedMappings();
sharedMappings.register(tsConfigPath, [
/* mapped paths to share */
], workspaceRootPath);
module.exports = {
output: {
uniqueName: \\"remote\\",
publicPath: \\"auto\\",
},
optimization: {
runtimeChunk: false,
},
experiments: {
outputModule: true
},
resolve: {
alias: {
...sharedMappings.getAliases(),
},
},
plugins: [
new ModuleFederationPlugin({
name: \\"remote\\",
filename: \\"remoteEntry.js\\",
exposes: {
'./Module': 'apps/remote/src/app/remote-entry/entry.module.ts',
},
shared: share({
\\"@angular/core\\": { singleton: true, strictVersion: true, requiredVersion: 'auto', includeSecondaries: true },
\\"@angular/common\\": { singleton: true, strictVersion: true, requiredVersion: 'auto', includeSecondaries: true },
\\"@angular/common/http\\": { singleton: true, strictVersion: true, requiredVersion: 'auto', includeSecondaries: true },
\\"@angular/router\\": { singleton: true, strictVersion: true, requiredVersion: 'auto', includeSecondaries: true },
\\"rxjs\\": { singleton: true, strictVersion: true, requiredVersion: 'auto', includeSecondaries: true },
...sharedMappings.getDescriptors(),
}),
library: {
type: 'module'
},
}),
sharedMappings.getPlugin(),
],
};
"
`;

exports[`MFE Host App Generator should generate a host mfe app with a remote 2`] = `
"const ModuleFederationPlugin = require(\\"webpack/lib/container/ModuleFederationPlugin\\");
const mf = require(\\"@angular-architects/module-federation/webpack\\");
const path = require(\\"path\\");
const share = mf.share;
/**
* We use the NX_TSCONFIG_PATH environment variable when using the @nrwl/angular:webpack-browser
* builder as it will generate a temporary tsconfig file which contains any required remappings of
* shared libraries.
* A remapping will occur when a library is buildable, as webpack needs to know the location of the
* built files for the buildable library.
* This NX_TSCONFIG_PATH environment variable is set by the @nrwl/angular:webpack-browser and it contains
* the location of the generated temporary tsconfig file.
*/
const tsConfigPath = process.env.NX_TSCONFIG_PATH ?? path.join(__dirname, '../../tsconfig.base.json');
const workspaceRootPath = path.join(__dirname, '../../');
const sharedMappings = new mf.SharedMappings();
sharedMappings.register(tsConfigPath, [
/* mapped paths to share */
], workspaceRootPath);
module.exports = {
output: {
uniqueName: \\"test\\",
publicPath: \\"auto\\",
},
optimization: {
runtimeChunk: false,
},
experiments: {
outputModule: true
},
resolve: {
alias: {
...sharedMappings.getAliases(),
},
},
plugins: [
new ModuleFederationPlugin({
remotes: {
\\"remote\\": \\"http://localhost:4201/remoteEntry.js\\",
},
shared: share({
\\"@angular/core\\": { singleton: true, strictVersion: true, requiredVersion: 'auto', includeSecondaries: true },
\\"@angular/common\\": { singleton: true, strictVersion: true, requiredVersion: 'auto', includeSecondaries: true },
\\"@angular/common/http\\": { singleton: true, strictVersion: true, requiredVersion: 'auto', includeSecondaries: true },
\\"@angular/router\\": { singleton: true, strictVersion: true, requiredVersion: 'auto', includeSecondaries: true },
\\"rxjs\\": { singleton: true, strictVersion: true, requiredVersion: 'auto', includeSecondaries: true },
...sharedMappings.getDescriptors(),
}),
library: {
type: 'module'
},
}),
sharedMappings.getPlugin(),
],
};
"
`;

exports[`MFE Host App Generator should generate a host mfe app with no remotes 1`] = `
"const ModuleFederationPlugin = require(\\"webpack/lib/container/ModuleFederationPlugin\\");
const mf = require(\\"@angular-architects/module-federation/webpack\\");
const path = require(\\"path\\");
const share = mf.share;
/**
* We use the NX_TSCONFIG_PATH environment variable when using the @nrwl/angular:webpack-browser
* builder as it will generate a temporary tsconfig file which contains any required remappings of
* shared libraries.
* A remapping will occur when a library is buildable, as webpack needs to know the location of the
* built files for the buildable library.
* This NX_TSCONFIG_PATH environment variable is set by the @nrwl/angular:webpack-browser and it contains
* the location of the generated temporary tsconfig file.
*/
const tsConfigPath = process.env.NX_TSCONFIG_PATH ?? path.join(__dirname, '../../tsconfig.base.json');
const workspaceRootPath = path.join(__dirname, '../../');
const sharedMappings = new mf.SharedMappings();
sharedMappings.register(tsConfigPath, [
/* mapped paths to share */
], workspaceRootPath);
module.exports = {
output: {
uniqueName: \\"test\\",
publicPath: \\"auto\\",
},
optimization: {
runtimeChunk: false,
},
experiments: {
outputModule: true
},
resolve: {
alias: {
...sharedMappings.getAliases(),
},
},
plugins: [
new ModuleFederationPlugin({
remotes: {
},
shared: share({
\\"@angular/core\\": { singleton: true, strictVersion: true, requiredVersion: 'auto', includeSecondaries: true },
\\"@angular/common\\": { singleton: true, strictVersion: true, requiredVersion: 'auto', includeSecondaries: true },
\\"@angular/common/http\\": { singleton: true, strictVersion: true, requiredVersion: 'auto', includeSecondaries: true },
\\"@angular/router\\": { singleton: true, strictVersion: true, requiredVersion: 'auto', includeSecondaries: true },
\\"rxjs\\": { singleton: true, strictVersion: true, requiredVersion: 'auto', includeSecondaries: true },
...sharedMappings.getDescriptors(),
}),
library: {
type: 'module'
},
}),
sharedMappings.getPlugin(),
],
};
"
`;
4 changes: 4 additions & 0 deletions packages/angular/src/generators/mfe-host/mfe-host.compat.ts
@@ -0,0 +1,4 @@
import { convertNxGenerator } from '@nrwl/devkit';
import mfeHost from './mfe-host';

export default convertNxGenerator(mfeHost);
61 changes: 61 additions & 0 deletions packages/angular/src/generators/mfe-host/mfe-host.spec.ts
@@ -0,0 +1,61 @@
import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing';
import mfeHost from './mfe-host';
import applicationGenerator from '../application/application';

describe('MFE Host App Generator', () => {
it('should generate a host mfe app with no remotes', async () => {
// ARRANGE
const tree = createTreeWithEmptyWorkspace(2);

// ACT
await mfeHost(tree, {
name: 'test',
});

// ASSERT
expect(tree.read('apps/test/webpack.config.js', 'utf-8')).toMatchSnapshot();
});

it('should generate a host mfe app with a remote', async () => {
// ARRANGE
const tree = createTreeWithEmptyWorkspace(2);

await applicationGenerator(tree, {
name: 'remote',
mfe: true,
mfeType: 'remote',
routing: true,
port: 4201,
});

// ACT
await mfeHost(tree, {
name: 'test',
remotes: ['remote'],
});

// ASSERT
expect(
tree.read('apps/remote/webpack.config.js', 'utf-8')
).toMatchSnapshot();
expect(tree.read('apps/test/webpack.config.js', 'utf-8')).toMatchSnapshot();
});

it('should error when a host app is attempted to be generated with an incorrect remote', async () => {
// ARRANGE
const tree = createTreeWithEmptyWorkspace(2);

// ACT
try {
await mfeHost(tree, {
name: 'test',
remotes: ['remote'],
});
} catch (error) {
// ASSERT
expect(error.message).toEqual(
'Could not find specified remote application (remote)'
);
}
});
});
30 changes: 30 additions & 0 deletions packages/angular/src/generators/mfe-host/mfe-host.ts
@@ -0,0 +1,30 @@
import type { Tree } from '@nrwl/devkit';
import type { Schema } from './schema';

import { getProjects } from '@nrwl/devkit';
import applicationGenerator from '../application/application';

export default async function mfeHost(tree: Tree, options: Schema) {
const projects = getProjects(tree);

if (options.remotes && options.remotes.length > 0) {
options.remotes.forEach((remote) => {
if (!projects.has(remote)) {
throw new Error(
`Could not find specified remote application (${remote})`
);
}
});
}

const installTask = await applicationGenerator(tree, {
name: options.name,
mfe: true,
mfeType: 'host',
routing: true,
remotes: options.remotes ?? [],
port: 4200,
});

return installTask;
}
4 changes: 4 additions & 0 deletions packages/angular/src/generators/mfe-host/schema.d.ts
@@ -0,0 +1,4 @@
export interface Schema {
name: string;
remotes?: string[];
}

1 comment on commit 7442030

@vercel
Copy link

@vercel vercel bot commented on 7442030 Mar 8, 2022

Choose a reason for hiding this comment

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

Please sign in to comment.