feat(angular): init mfe generator (#6209)
Add generator to scaffold Module Federation configuration for a given Angular application
This commit is contained in:
parent
80d3999d42
commit
dd51c18d12
59
docs/angular/api-angular/generators/setup-mfe.md
Normal file
59
docs/angular/api-angular/generators/setup-mfe.md
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
# setup-mfe
|
||||||
|
|
||||||
|
Generate a Module Federation configuration for a given Angular application.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nx generate setup-mfe ...
|
||||||
|
```
|
||||||
|
|
||||||
|
By default, Nx will search for `setup-mfe` in the default collection provisioned in `angular.json`.
|
||||||
|
|
||||||
|
You can specify the collection explicitly as follows:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nx g @nrwl/angular:setup-mfe ...
|
||||||
|
```
|
||||||
|
|
||||||
|
Show what will be generated without writing to disk:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nx g setup-mfe ... --dry-run
|
||||||
|
```
|
||||||
|
|
||||||
|
## Options
|
||||||
|
|
||||||
|
### appName
|
||||||
|
|
||||||
|
Type: `string`
|
||||||
|
|
||||||
|
The name of the application to generate the Module Federation configuration for.
|
||||||
|
|
||||||
|
### mfeType
|
||||||
|
|
||||||
|
Default: `shell`
|
||||||
|
|
||||||
|
Type: `string`
|
||||||
|
|
||||||
|
Possible values: `shell`, `remote`
|
||||||
|
|
||||||
|
Type of application to generate the Module Federation configuration for.
|
||||||
|
|
||||||
|
### port
|
||||||
|
|
||||||
|
Type: `number`
|
||||||
|
|
||||||
|
The port at which the remote application should be served.
|
||||||
|
|
||||||
|
### remotes
|
||||||
|
|
||||||
|
Type: `array`
|
||||||
|
|
||||||
|
A list of remote application names that the shell application should consume.
|
||||||
|
|
||||||
|
### skipFormat
|
||||||
|
|
||||||
|
Type: `boolean`
|
||||||
|
|
||||||
|
Skip formatting the workspace after the generator completes.
|
||||||
@ -1031,6 +1031,8 @@ Reads a project configuration.
|
|||||||
The project configuration is stored in workspace.json and nx.json. The utility will read
|
The project configuration is stored in workspace.json and nx.json. The utility will read
|
||||||
both files.
|
both files.
|
||||||
|
|
||||||
|
**`throws`** If supplied projectName cannot be found
|
||||||
|
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | Type | Description |
|
| Name | Type | Description |
|
||||||
|
|||||||
@ -422,6 +422,11 @@
|
|||||||
"id": "ngrx",
|
"id": "ngrx",
|
||||||
"file": "angular/api-angular/generators/ngrx"
|
"file": "angular/api-angular/generators/ngrx"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "setup-mfe generator",
|
||||||
|
"id": "setup-mfe",
|
||||||
|
"file": "angular/api-angular/generators/setup-mfe"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "stories generator",
|
"name": "stories generator",
|
||||||
"id": "stories",
|
"id": "stories",
|
||||||
@ -1558,6 +1563,11 @@
|
|||||||
"id": "ngrx",
|
"id": "ngrx",
|
||||||
"file": "react/api-angular/generators/ngrx"
|
"file": "react/api-angular/generators/ngrx"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "setup-mfe generator",
|
||||||
|
"id": "setup-mfe",
|
||||||
|
"file": "react/api-angular/generators/setup-mfe"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "stories generator",
|
"name": "stories generator",
|
||||||
"id": "stories",
|
"id": "stories",
|
||||||
@ -2658,6 +2668,11 @@
|
|||||||
"id": "ngrx",
|
"id": "ngrx",
|
||||||
"file": "node/api-angular/generators/ngrx"
|
"file": "node/api-angular/generators/ngrx"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "setup-mfe generator",
|
||||||
|
"id": "setup-mfe",
|
||||||
|
"file": "node/api-angular/generators/setup-mfe"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "stories generator",
|
"name": "stories generator",
|
||||||
"id": "stories",
|
"id": "stories",
|
||||||
|
|||||||
59
docs/node/api-angular/generators/setup-mfe.md
Normal file
59
docs/node/api-angular/generators/setup-mfe.md
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
# setup-mfe
|
||||||
|
|
||||||
|
Generate a Module Federation configuration for a given Angular application.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nx generate setup-mfe ...
|
||||||
|
```
|
||||||
|
|
||||||
|
By default, Nx will search for `setup-mfe` in the default collection provisioned in `workspace.json`.
|
||||||
|
|
||||||
|
You can specify the collection explicitly as follows:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nx g @nrwl/angular:setup-mfe ...
|
||||||
|
```
|
||||||
|
|
||||||
|
Show what will be generated without writing to disk:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nx g setup-mfe ... --dry-run
|
||||||
|
```
|
||||||
|
|
||||||
|
## Options
|
||||||
|
|
||||||
|
### appName
|
||||||
|
|
||||||
|
Type: `string`
|
||||||
|
|
||||||
|
The name of the application to generate the Module Federation configuration for.
|
||||||
|
|
||||||
|
### mfeType
|
||||||
|
|
||||||
|
Default: `shell`
|
||||||
|
|
||||||
|
Type: `string`
|
||||||
|
|
||||||
|
Possible values: `shell`, `remote`
|
||||||
|
|
||||||
|
Type of application to generate the Module Federation configuration for.
|
||||||
|
|
||||||
|
### port
|
||||||
|
|
||||||
|
Type: `number`
|
||||||
|
|
||||||
|
The port at which the remote application should be served.
|
||||||
|
|
||||||
|
### remotes
|
||||||
|
|
||||||
|
Type: `array`
|
||||||
|
|
||||||
|
A list of remote application names that the shell application should consume.
|
||||||
|
|
||||||
|
### skipFormat
|
||||||
|
|
||||||
|
Type: `boolean`
|
||||||
|
|
||||||
|
Skip formatting the workspace after the generator completes.
|
||||||
@ -1031,6 +1031,8 @@ Reads a project configuration.
|
|||||||
The project configuration is stored in workspace.json and nx.json. The utility will read
|
The project configuration is stored in workspace.json and nx.json. The utility will read
|
||||||
both files.
|
both files.
|
||||||
|
|
||||||
|
**`throws`** If supplied projectName cannot be found
|
||||||
|
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | Type | Description |
|
| Name | Type | Description |
|
||||||
|
|||||||
59
docs/react/api-angular/generators/setup-mfe.md
Normal file
59
docs/react/api-angular/generators/setup-mfe.md
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
# setup-mfe
|
||||||
|
|
||||||
|
Generate a Module Federation configuration for a given Angular application.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nx generate setup-mfe ...
|
||||||
|
```
|
||||||
|
|
||||||
|
By default, Nx will search for `setup-mfe` in the default collection provisioned in `workspace.json`.
|
||||||
|
|
||||||
|
You can specify the collection explicitly as follows:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nx g @nrwl/angular:setup-mfe ...
|
||||||
|
```
|
||||||
|
|
||||||
|
Show what will be generated without writing to disk:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nx g setup-mfe ... --dry-run
|
||||||
|
```
|
||||||
|
|
||||||
|
## Options
|
||||||
|
|
||||||
|
### appName
|
||||||
|
|
||||||
|
Type: `string`
|
||||||
|
|
||||||
|
The name of the application to generate the Module Federation configuration for.
|
||||||
|
|
||||||
|
### mfeType
|
||||||
|
|
||||||
|
Default: `shell`
|
||||||
|
|
||||||
|
Type: `string`
|
||||||
|
|
||||||
|
Possible values: `shell`, `remote`
|
||||||
|
|
||||||
|
Type of application to generate the Module Federation configuration for.
|
||||||
|
|
||||||
|
### port
|
||||||
|
|
||||||
|
Type: `number`
|
||||||
|
|
||||||
|
The port at which the remote application should be served.
|
||||||
|
|
||||||
|
### remotes
|
||||||
|
|
||||||
|
Type: `array`
|
||||||
|
|
||||||
|
A list of remote application names that the shell application should consume.
|
||||||
|
|
||||||
|
### skipFormat
|
||||||
|
|
||||||
|
Type: `boolean`
|
||||||
|
|
||||||
|
Skip formatting the workspace after the generator completes.
|
||||||
@ -1031,6 +1031,8 @@ Reads a project configuration.
|
|||||||
The project configuration is stored in workspace.json and nx.json. The utility will read
|
The project configuration is stored in workspace.json and nx.json. The utility will read
|
||||||
both files.
|
both files.
|
||||||
|
|
||||||
|
**`throws`** If supplied projectName cannot be found
|
||||||
|
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | Type | Description |
|
| Name | Type | Description |
|
||||||
|
|||||||
@ -122,6 +122,7 @@ myorg/
|
|||||||
- [library](/{{framework}}/angular/library) - Creates an Angular library.
|
- [library](/{{framework}}/angular/library) - Creates an Angular library.
|
||||||
- [move](/{{framework}}/angular/move) - Moves an Angular application or library to another folder within the workspace and updates the project configuration.
|
- [move](/{{framework}}/angular/move) - Moves an Angular application or library to another folder within the workspace and updates the project configuration.
|
||||||
- [ngrx](/{{framework}}/angular/ngrx) - Adds NgRx support to an application or library.
|
- [ngrx](/{{framework}}/angular/ngrx) - Adds NgRx support to an application or library.
|
||||||
|
- [setup-mfe](/{{framework}}/angular/setup-mfe) - Generate a Module Federation configuration for a given Angular application.
|
||||||
- [stories](/{{framework}}/angular/stories) - Creates stories/specs for all components declared in a project.
|
- [stories](/{{framework}}/angular/stories) - Creates stories/specs for all components declared in a project.
|
||||||
- [storybook-configuration](/{{framework}}/angular/storybook-configuration) - Adds Storybook configuration to a project.
|
- [storybook-configuration](/{{framework}}/angular/storybook-configuration) - Adds Storybook configuration to a project.
|
||||||
- [storybook-migrate-defaults-5-to-6](/{{framework}}/angular/storybook-migrate-defaults-5-to-6) - Generates default Storybook configuration files using Storybook version >=6.x specs, for projects that already have Storybook instances and configurations of versions <6.x.
|
- [storybook-migrate-defaults-5-to-6](/{{framework}}/angular/storybook-migrate-defaults-5-to-6) - Generates default Storybook configuration files using Storybook version >=6.x specs, for projects that already have Storybook instances and configurations of versions <6.x.
|
||||||
|
|||||||
@ -84,7 +84,6 @@
|
|||||||
"@storybook/angular": "~6.3.0",
|
"@storybook/angular": "~6.3.0",
|
||||||
"@storybook/core": "~6.3.0",
|
"@storybook/core": "~6.3.0",
|
||||||
"@storybook/react": "~6.3.0",
|
"@storybook/react": "~6.3.0",
|
||||||
"@svgr/webpack": "^5.4.0",
|
|
||||||
"@svgr/webpack": "5.5.0",
|
"@svgr/webpack": "5.5.0",
|
||||||
"@tailwindcss/typography": "^0.4.0",
|
"@tailwindcss/typography": "^0.4.0",
|
||||||
"@testing-library/react": "11.2.5",
|
"@testing-library/react": "11.2.5",
|
||||||
@ -279,4 +278,4 @@
|
|||||||
"ng-packagr/rxjs": "6.6.7",
|
"ng-packagr/rxjs": "6.6.7",
|
||||||
"**/xmlhttprequest-ssl": "~1.6.2"
|
"**/xmlhttprequest-ssl": "~1.6.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -96,6 +96,13 @@
|
|||||||
"schema": "./src/generators/upgrade-module/schema.json",
|
"schema": "./src/generators/upgrade-module/schema.json",
|
||||||
"description": "Sets up an Upgrade Module."
|
"description": "Sets up an Upgrade Module."
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"setup-mfe": {
|
||||||
|
"factory": "./src/generators/setup-mfe/compat",
|
||||||
|
"schema": "./src/generators/setup-mfe/schema.json",
|
||||||
|
"description": "Generate a Module Federation configuration for a given Angular application."
|
||||||
|
},
|
||||||
|
|
||||||
"web-worker": {
|
"web-worker": {
|
||||||
"factory": "./src/generators/web-worker/compat",
|
"factory": "./src/generators/web-worker/compat",
|
||||||
"schema": "./src/generators/web-worker/schema.json",
|
"schema": "./src/generators/web-worker/schema.json",
|
||||||
@ -115,6 +122,11 @@
|
|||||||
"aliases": ["app"],
|
"aliases": ["app"],
|
||||||
"description": "Creates an Angular application."
|
"description": "Creates an Angular application."
|
||||||
},
|
},
|
||||||
|
"setup-mfe": {
|
||||||
|
"factory": "./src/generators/setup-mfe/setup-mfe",
|
||||||
|
"schema": "./src/generators/setup-mfe/schema.json",
|
||||||
|
"description": "Generate a Module Federation configuration for a given Angular application."
|
||||||
|
},
|
||||||
"component-cypress-spec": {
|
"component-cypress-spec": {
|
||||||
"factory": "./src/generators/component-cypress-spec/component-cypress-spec",
|
"factory": "./src/generators/component-cypress-spec/component-cypress-spec",
|
||||||
"schema": "./src/generators/component-cypress-spec/schema.json",
|
"schema": "./src/generators/component-cypress-spec/schema.json",
|
||||||
|
|||||||
@ -11,3 +11,4 @@ export * from './src/generators/storybook-configuration/storybook-configuration'
|
|||||||
export * from './src/generators/storybook-migrate-defaults-5-to-6/storybook-migrate-defaults-5-to-6';
|
export * from './src/generators/storybook-migrate-defaults-5-to-6/storybook-migrate-defaults-5-to-6';
|
||||||
export * from './src/generators/storybook-migrate-stories-to-6-2/migrate-stories-to-6-2';
|
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/upgrade-module/upgrade-module';
|
||||||
|
export * from './src/generators/setup-mfe/setup-mfe';
|
||||||
|
|||||||
@ -0,0 +1,89 @@
|
|||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`Init MFE should create webpack configs correctly 1`] = `
|
||||||
|
"const ModuleFederationPlugin = require(\\"webpack/lib/container/ModuleFederationPlugin\\");
|
||||||
|
const mf = require(\\"@angular-architects/module-federation/webpack\\");
|
||||||
|
const path = require(\\"path\\");
|
||||||
|
|
||||||
|
const sharedMappings = new mf.SharedMappings();
|
||||||
|
sharedMappings.register(path.join(__dirname, \\"../../tsconfig.base.json\\"), [
|
||||||
|
/* mapped paths to share */
|
||||||
|
]);
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
output: {
|
||||||
|
uniqueName: \\"app1\\",
|
||||||
|
publicPath: \\"auto\\",
|
||||||
|
},
|
||||||
|
optimization: {
|
||||||
|
runtimeChunk: false,
|
||||||
|
minimize: false,
|
||||||
|
},
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
...sharedMappings.getAliases(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
new ModuleFederationPlugin({
|
||||||
|
remotes: {
|
||||||
|
|
||||||
|
},
|
||||||
|
shared: {
|
||||||
|
\\"@angular/core\\": { singleton: true, strictVersion: true },
|
||||||
|
\\"@angular/common\\": { singleton: true, strictVersion: true },
|
||||||
|
\\"@angular/common/http\\": { singleton: true, strictVersion: true },
|
||||||
|
\\"@angular/router\\": { singleton: true, strictVersion: true },
|
||||||
|
...sharedMappings.getDescriptors(),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
sharedMappings.getPlugin(),
|
||||||
|
],
|
||||||
|
};
|
||||||
|
"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Init MFE should create webpack configs correctly 2`] = `
|
||||||
|
"const ModuleFederationPlugin = require(\\"webpack/lib/container/ModuleFederationPlugin\\");
|
||||||
|
const mf = require(\\"@angular-architects/module-federation/webpack\\");
|
||||||
|
const path = require(\\"path\\");
|
||||||
|
|
||||||
|
const sharedMappings = new mf.SharedMappings();
|
||||||
|
sharedMappings.register(path.join(__dirname, \\"../../tsconfig.base.json\\"), [
|
||||||
|
/* mapped paths to share */
|
||||||
|
]);
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
output: {
|
||||||
|
uniqueName: \\"remote1\\",
|
||||||
|
publicPath: \\"auto\\",
|
||||||
|
},
|
||||||
|
optimization: {
|
||||||
|
runtimeChunk: false,
|
||||||
|
minimize: false,
|
||||||
|
},
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
...sharedMappings.getAliases(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
new ModuleFederationPlugin({
|
||||||
|
name: \\"remote1\\",
|
||||||
|
filename: \\"remoteEntry.js\\",
|
||||||
|
exposes: {
|
||||||
|
'./Component': 'apps/remote1/src/app/app.component.ts',
|
||||||
|
},
|
||||||
|
shared: {
|
||||||
|
\\"@angular/core\\": { singleton: true, strictVersion: true },
|
||||||
|
\\"@angular/common\\": { singleton: true, strictVersion: true },
|
||||||
|
\\"@angular/common/http\\": { singleton: true, strictVersion: true },
|
||||||
|
\\"@angular/router\\": { singleton: true, strictVersion: true },
|
||||||
|
...sharedMappings.getDescriptors(),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
sharedMappings.getPlugin(),
|
||||||
|
],
|
||||||
|
};
|
||||||
|
"
|
||||||
|
`;
|
||||||
@ -0,0 +1,41 @@
|
|||||||
|
const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin");
|
||||||
|
const mf = require("@angular-architects/module-federation/webpack");
|
||||||
|
const path = require("path");
|
||||||
|
|
||||||
|
const sharedMappings = new mf.SharedMappings();
|
||||||
|
sharedMappings.register(path.join(__dirname, "../../tsconfig.base.json"), [
|
||||||
|
/* mapped paths to share */
|
||||||
|
]);
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
output: {
|
||||||
|
uniqueName: "<%= name %>",
|
||||||
|
publicPath: "auto",
|
||||||
|
},
|
||||||
|
optimization: {
|
||||||
|
runtimeChunk: false,
|
||||||
|
minimize: false,
|
||||||
|
},
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
...sharedMappings.getAliases(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
new ModuleFederationPlugin({<% if(type === 'remote') { %>
|
||||||
|
name: "<%= name %>",
|
||||||
|
filename: "remoteEntry.js",
|
||||||
|
exposes: {
|
||||||
|
'./Component': '<%= sourceRoot %>/src/app/app.component.ts',
|
||||||
|
},<% } %><% if(type === 'shell') { %>
|
||||||
|
remotes: {
|
||||||
|
<% remotes.forEach(function(remote) { %>"<%= remote.remoteName %>": "<%= remote.remoteName %>@http://localhost:<%= remote.port %>/remoteEntry.js",<% }); %>
|
||||||
|
},<% } %>
|
||||||
|
shared: {<% sharedLibraries.forEach(function (lib) { %>
|
||||||
|
"<%= lib %>": { singleton: true, strictVersion: true },<% }); %>
|
||||||
|
...sharedMappings.getDescriptors(),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
sharedMappings.getPlugin(),
|
||||||
|
],
|
||||||
|
};
|
||||||
@ -0,0 +1 @@
|
|||||||
|
module.exports = require('./webpack.config');
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
import type { Tree } from '@nrwl/devkit';
|
||||||
|
import type { Schema } from '../schema';
|
||||||
|
|
||||||
|
import {
|
||||||
|
readProjectConfiguration,
|
||||||
|
updateProjectConfiguration,
|
||||||
|
} from '@nrwl/devkit';
|
||||||
|
|
||||||
|
export function addImplicitDeps(host: Tree, options: Schema) {
|
||||||
|
if (
|
||||||
|
options.mfeType === 'shell' &&
|
||||||
|
Array.isArray(options.remotes) &&
|
||||||
|
options.remotes.length > 0
|
||||||
|
) {
|
||||||
|
const appConfig = readProjectConfiguration(host, options.appName);
|
||||||
|
appConfig.implicitDependencies = Array.isArray(
|
||||||
|
appConfig.implicitDependencies
|
||||||
|
)
|
||||||
|
? [...appConfig.implicitDependencies, ...options.remotes]
|
||||||
|
: [...options.remotes];
|
||||||
|
updateProjectConfiguration(host, options.appName, appConfig);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,28 @@
|
|||||||
|
import type { Tree } from '@nrwl/devkit';
|
||||||
|
import type { Schema } from '../schema';
|
||||||
|
|
||||||
|
import {
|
||||||
|
readProjectConfiguration,
|
||||||
|
updateProjectConfiguration,
|
||||||
|
} from '@nrwl/devkit';
|
||||||
|
|
||||||
|
export function changeBuildTarget(host: Tree, options: Schema) {
|
||||||
|
const appConfig = readProjectConfiguration(host, options.appName);
|
||||||
|
|
||||||
|
appConfig.targets.build.executor = '@nrwl/angular:webpack-browser';
|
||||||
|
appConfig.targets.build.options = {
|
||||||
|
...appConfig.targets.build.options,
|
||||||
|
customWebpackConfig: {
|
||||||
|
path: `${appConfig.root}/webpack.config.js`,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
appConfig.targets.build.configurations.production = {
|
||||||
|
...appConfig.targets.build.configurations.production,
|
||||||
|
customWebpackConfig: {
|
||||||
|
path: `${appConfig.root}/webpack.prod.config.js`,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
updateProjectConfiguration(host, options.appName, appConfig);
|
||||||
|
}
|
||||||
@ -0,0 +1,15 @@
|
|||||||
|
import type { Tree } from '@nrwl/devkit';
|
||||||
|
import type { Schema } from '../schema';
|
||||||
|
|
||||||
|
import { joinPathFragments } from '@nrwl/devkit';
|
||||||
|
|
||||||
|
export function fixBootstrap(host: Tree, appRoot: string) {
|
||||||
|
const mainFilePath = joinPathFragments(appRoot, 'src/main.ts');
|
||||||
|
const bootstrapCode = host.read(mainFilePath, 'utf-8');
|
||||||
|
host.write(joinPathFragments(appRoot, 'src/bootstrap.ts'), bootstrapCode);
|
||||||
|
|
||||||
|
host.write(
|
||||||
|
mainFilePath,
|
||||||
|
`import('./bootstrap').catch(err => console.error(err));`
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -0,0 +1,35 @@
|
|||||||
|
import type { Tree } from '@nrwl/devkit';
|
||||||
|
import type { Schema } from '../schema';
|
||||||
|
import { generateFiles, joinPathFragments, logger } from '@nrwl/devkit';
|
||||||
|
|
||||||
|
const SHARED_SINGLETON_LIBRARIES = [
|
||||||
|
'@angular/core',
|
||||||
|
'@angular/common',
|
||||||
|
'@angular/common/http',
|
||||||
|
'@angular/router',
|
||||||
|
];
|
||||||
|
|
||||||
|
export function generateWebpackConfig(
|
||||||
|
host: Tree,
|
||||||
|
options: Schema,
|
||||||
|
appRoot: string,
|
||||||
|
remotesWithPorts: { remoteName: string; port: number }[]
|
||||||
|
) {
|
||||||
|
if (
|
||||||
|
host.exists(`${appRoot}/webpack.config.js`) ||
|
||||||
|
host.exists(`${appRoot}/webpack.prod.config.js`)
|
||||||
|
) {
|
||||||
|
logger.warn(
|
||||||
|
`NOTE: We encountered an existing webpack config for the app ${options.appName}. We have overwritten this file with the Module Federation Config.\n
|
||||||
|
If this was not the outcome you expected, you can discard the changes we have made, create a backup of your current webpack config, and run the command again.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
generateFiles(host, joinPathFragments(__dirname, '../files'), appRoot, {
|
||||||
|
tmpl: '',
|
||||||
|
type: options.mfeType,
|
||||||
|
name: options.appName,
|
||||||
|
remotes: remotesWithPorts ?? [],
|
||||||
|
sourceRoot: appRoot,
|
||||||
|
sharedLibraries: SHARED_SINGLETON_LIBRARIES,
|
||||||
|
});
|
||||||
|
}
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
import type { Tree } from '@nrwl/devkit';
|
||||||
|
import type { Schema } from '../schema';
|
||||||
|
|
||||||
|
import { readProjectConfiguration } from '@nrwl/devkit';
|
||||||
|
|
||||||
|
export function getRemotesWithPorts(host: Tree, options: Schema) {
|
||||||
|
// If type is shell and remotes supplied, check remotes exist
|
||||||
|
const remotesWithPort: { remoteName: string; port: number }[] = [];
|
||||||
|
if (
|
||||||
|
options.mfeType === 'shell' &&
|
||||||
|
Array.isArray(options.remotes) &&
|
||||||
|
options.remotes.length > 0
|
||||||
|
) {
|
||||||
|
for (const remote of options.remotes) {
|
||||||
|
const remoteConfig = readProjectConfiguration(host, remote);
|
||||||
|
remotesWithPort.push({
|
||||||
|
remoteName: remote,
|
||||||
|
port: remoteConfig.targets['mfe-serve']?.options.port ?? 4200,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return remotesWithPort;
|
||||||
|
}
|
||||||
6
packages/angular/src/generators/setup-mfe/lib/index.ts
Normal file
6
packages/angular/src/generators/setup-mfe/lib/index.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
export * from './add-implicit-deps';
|
||||||
|
export * from './change-build-target';
|
||||||
|
export * from './fix-bootstrap';
|
||||||
|
export * from './generate-config';
|
||||||
|
export * from './get-remotes-with-ports';
|
||||||
|
export * from './setup-serve-target';
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
import type { Tree } from '@nrwl/devkit';
|
||||||
|
import type { Schema } from '../schema';
|
||||||
|
|
||||||
|
import {
|
||||||
|
readProjectConfiguration,
|
||||||
|
updateProjectConfiguration,
|
||||||
|
} from '@nrwl/devkit';
|
||||||
|
|
||||||
|
export function setupServeTarget(host: Tree, options: Schema) {
|
||||||
|
if (options.mfeType === 'remote') {
|
||||||
|
const appConfig = readProjectConfiguration(host, options.appName);
|
||||||
|
|
||||||
|
const port = options.port ?? 4200;
|
||||||
|
|
||||||
|
appConfig.targets['mfe-serve'] = {
|
||||||
|
executor: '@nrwl/workspace:run-commands',
|
||||||
|
options: {
|
||||||
|
command: `nx serve ${options.appName}`,
|
||||||
|
port,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
updateProjectConfiguration(host, options.appName, appConfig);
|
||||||
|
}
|
||||||
|
}
|
||||||
7
packages/angular/src/generators/setup-mfe/schema.d.ts
vendored
Normal file
7
packages/angular/src/generators/setup-mfe/schema.d.ts
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
export interface Schema {
|
||||||
|
appName: string;
|
||||||
|
mfeType: 'shell' | 'remote';
|
||||||
|
port?: number;
|
||||||
|
remotes?: string[];
|
||||||
|
skipFormat?: boolean;
|
||||||
|
}
|
||||||
39
packages/angular/src/generators/setup-mfe/schema.json
Normal file
39
packages/angular/src/generators/setup-mfe/schema.json
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/schema",
|
||||||
|
"$id": "GeneratorAngularMFESetup",
|
||||||
|
"cli": "nx",
|
||||||
|
"title": "Generate Module Federation Setup for Angular App",
|
||||||
|
"description": "Create Module Federation configuration files for given Angular Application.",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"appName": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The name of the application to generate the Module Federation configuration for.",
|
||||||
|
"$default": {
|
||||||
|
"$source": "argv",
|
||||||
|
"index": 0
|
||||||
|
},
|
||||||
|
"x-prompt": "What app would you like to generate a Module Federation configuration for?"
|
||||||
|
},
|
||||||
|
"mfeType": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["shell", "remote"],
|
||||||
|
"description": "Type of application to generate the Module Federation configuration for.",
|
||||||
|
"default": "shell"
|
||||||
|
},
|
||||||
|
"port": {
|
||||||
|
"type": "number",
|
||||||
|
"description": "The port at which the remote application should be served."
|
||||||
|
},
|
||||||
|
"remotes": {
|
||||||
|
"type": "array",
|
||||||
|
"description": "A list of remote application names that the shell application should consume."
|
||||||
|
},
|
||||||
|
"skipFormat": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Skip formatting the workspace after the generator completes."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["appName", "mfeType"],
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
||||||
@ -0,0 +1,4 @@
|
|||||||
|
import { convertNxGenerator } from '@nrwl/devkit';
|
||||||
|
import { setupMfe } from './setup-mfe';
|
||||||
|
|
||||||
|
export default convertNxGenerator(setupMfe);
|
||||||
167
packages/angular/src/generators/setup-mfe/setup-mfe.spec.ts
Normal file
167
packages/angular/src/generators/setup-mfe/setup-mfe.spec.ts
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
import type { NxJsonConfiguration, Tree } from '@nrwl/devkit';
|
||||||
|
import { readJson, readProjectConfiguration } from '@nrwl/devkit';
|
||||||
|
import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing';
|
||||||
|
|
||||||
|
import { setupMfe } from './setup-mfe';
|
||||||
|
import applicationGenerator from '../application/application';
|
||||||
|
describe('Init MFE', () => {
|
||||||
|
let host: Tree;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
host = createTreeWithEmptyWorkspace();
|
||||||
|
await applicationGenerator(host, {
|
||||||
|
name: 'app1',
|
||||||
|
});
|
||||||
|
await applicationGenerator(host, {
|
||||||
|
name: 'remote1',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test.each([
|
||||||
|
['app1', 'shell'],
|
||||||
|
['remote1', 'remote'],
|
||||||
|
])(
|
||||||
|
'should create webpack configs correctly',
|
||||||
|
async (app, type: 'shell' | 'remote') => {
|
||||||
|
// ACT
|
||||||
|
await setupMfe(host, {
|
||||||
|
appName: app,
|
||||||
|
mfeType: type,
|
||||||
|
});
|
||||||
|
|
||||||
|
// ASSERT
|
||||||
|
expect(host.exists(`apps/${app}/webpack.config.js`)).toBeTruthy();
|
||||||
|
expect(host.exists(`apps/${app}/webpack.prod.config.js`)).toBeTruthy();
|
||||||
|
|
||||||
|
const webpackContetnts = host.read(
|
||||||
|
`apps/${app}/webpack.config.js`,
|
||||||
|
'utf-8'
|
||||||
|
);
|
||||||
|
expect(webpackContetnts).toMatchSnapshot();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
test.each([
|
||||||
|
['app1', 'shell'],
|
||||||
|
['remote1', 'remote'],
|
||||||
|
])(
|
||||||
|
'create bootstrap file with the contents of main.ts',
|
||||||
|
async (app, type: 'shell' | 'remote') => {
|
||||||
|
// ARRANGE
|
||||||
|
const mainContents = host.read(`apps/${app}/src/main.ts`, 'utf-8');
|
||||||
|
|
||||||
|
// ACT
|
||||||
|
await setupMfe(host, {
|
||||||
|
appName: app,
|
||||||
|
mfeType: type,
|
||||||
|
});
|
||||||
|
|
||||||
|
// ASSERT
|
||||||
|
const bootstrapContents = host.read(
|
||||||
|
`apps/${app}/src/bootstrap.ts`,
|
||||||
|
'utf-8'
|
||||||
|
);
|
||||||
|
const updatedMainContents = host.read(`apps/${app}/src/main.ts`, 'utf-8');
|
||||||
|
|
||||||
|
expect(bootstrapContents).toEqual(mainContents);
|
||||||
|
expect(updatedMainContents).not.toEqual(mainContents);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
test.each([
|
||||||
|
['app1', 'shell'],
|
||||||
|
['remote1', 'remote'],
|
||||||
|
])(
|
||||||
|
'should alter main.ts to import the bootstrap file dynamically',
|
||||||
|
async (app, type: 'shell' | 'remote') => {
|
||||||
|
// ARRANGE
|
||||||
|
const mainContents = host.read(`apps/${app}/src/main.ts`, 'utf-8');
|
||||||
|
|
||||||
|
// ACT
|
||||||
|
await setupMfe(host, {
|
||||||
|
appName: app,
|
||||||
|
mfeType: type,
|
||||||
|
});
|
||||||
|
|
||||||
|
// ASSERT
|
||||||
|
const updatedMainContents = host.read(`apps/${app}/src/main.ts`, 'utf-8');
|
||||||
|
|
||||||
|
expect(updatedMainContents).toEqual(
|
||||||
|
`import('./bootstrap').catch(err => console.error(err));`
|
||||||
|
);
|
||||||
|
expect(updatedMainContents).not.toEqual(mainContents);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
test.each([
|
||||||
|
['app1', 'shell'],
|
||||||
|
['remote1', 'remote'],
|
||||||
|
])(
|
||||||
|
'should change the build target and set correct path to webpack config',
|
||||||
|
async (app, type: 'shell' | 'remote') => {
|
||||||
|
// ACT
|
||||||
|
await setupMfe(host, {
|
||||||
|
appName: app,
|
||||||
|
mfeType: type,
|
||||||
|
});
|
||||||
|
|
||||||
|
// ASSERT
|
||||||
|
const { build } = readProjectConfiguration(host, app).targets;
|
||||||
|
|
||||||
|
expect(build.executor).toEqual('@nrwl/angular:webpack-browser');
|
||||||
|
expect(build.options.customWebpackConfig.path).toEqual(
|
||||||
|
`apps/${app}/webpack.config.js`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
test.each([
|
||||||
|
['app1', 'shell'],
|
||||||
|
['remote1', 'remote'],
|
||||||
|
])(
|
||||||
|
'should install @angular-architects/module-federation in the monorepo',
|
||||||
|
async (app, type: 'shell' | 'remote') => {
|
||||||
|
// ACT
|
||||||
|
await setupMfe(host, {
|
||||||
|
appName: app,
|
||||||
|
mfeType: type,
|
||||||
|
});
|
||||||
|
|
||||||
|
// ASSERT
|
||||||
|
const { dependencies } = readJson(host, 'package.json');
|
||||||
|
|
||||||
|
expect(
|
||||||
|
dependencies['@angular-architects/module-federation']
|
||||||
|
).toBeTruthy();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
it('should add the remote config to the shell when --remotes flag supplied', async () => {
|
||||||
|
// ACT
|
||||||
|
await setupMfe(host, {
|
||||||
|
appName: 'app1',
|
||||||
|
mfeType: 'shell',
|
||||||
|
remotes: ['remote1'],
|
||||||
|
});
|
||||||
|
|
||||||
|
// ASSERT
|
||||||
|
const webpackContents = host.read(`apps/app1/webpack.config.js`, 'utf-8');
|
||||||
|
|
||||||
|
expect(webpackContents).toContain(
|
||||||
|
'"remote1": "remote1@http://localhost:4200/remoteEntry.js"'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it('should update the implicit dependencies of the shell when --remotes flag supplied', async () => {
|
||||||
|
// ACT
|
||||||
|
await setupMfe(host, {
|
||||||
|
appName: 'app1',
|
||||||
|
mfeType: 'shell',
|
||||||
|
remotes: ['remote1'],
|
||||||
|
});
|
||||||
|
|
||||||
|
// ASSERT
|
||||||
|
const nxJson: NxJsonConfiguration = readJson(host, 'nx.json');
|
||||||
|
|
||||||
|
expect(nxJson.projects['app1'].implicitDependencies).toContain('remote1');
|
||||||
|
});
|
||||||
|
});
|
||||||
47
packages/angular/src/generators/setup-mfe/setup-mfe.ts
Normal file
47
packages/angular/src/generators/setup-mfe/setup-mfe.ts
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
import type { Tree } from '@nrwl/devkit';
|
||||||
|
import type { Schema } from './schema';
|
||||||
|
|
||||||
|
import {
|
||||||
|
readProjectConfiguration,
|
||||||
|
addDependenciesToPackageJson,
|
||||||
|
formatFiles,
|
||||||
|
} from '@nrwl/devkit';
|
||||||
|
|
||||||
|
import {
|
||||||
|
addImplicitDeps,
|
||||||
|
changeBuildTarget,
|
||||||
|
fixBootstrap,
|
||||||
|
generateWebpackConfig,
|
||||||
|
getRemotesWithPorts,
|
||||||
|
setupServeTarget,
|
||||||
|
} from './lib';
|
||||||
|
|
||||||
|
export async function setupMfe(host: Tree, options: Schema) {
|
||||||
|
const projectConfig = readProjectConfiguration(host, options.appName);
|
||||||
|
|
||||||
|
const remotesWithPorts = getRemotesWithPorts(host, options);
|
||||||
|
|
||||||
|
generateWebpackConfig(host, options, projectConfig.root, remotesWithPorts);
|
||||||
|
|
||||||
|
addImplicitDeps(host, options);
|
||||||
|
changeBuildTarget(host, options);
|
||||||
|
setupServeTarget(host, options);
|
||||||
|
|
||||||
|
fixBootstrap(host, projectConfig.root);
|
||||||
|
|
||||||
|
// add package to install
|
||||||
|
const installPackages = addDependenciesToPackageJson(
|
||||||
|
host,
|
||||||
|
{ '@angular-architects/module-federation': '^12.2.0' },
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
|
||||||
|
// format files
|
||||||
|
if (!options.skipFormat) {
|
||||||
|
await formatFiles(host);
|
||||||
|
}
|
||||||
|
|
||||||
|
return installPackages;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default setupMfe;
|
||||||
@ -151,6 +151,7 @@ export function updateWorkspaceConfiguration(
|
|||||||
*
|
*
|
||||||
* @param host - the file system tree
|
* @param host - the file system tree
|
||||||
* @param projectName - unique name. Often directories are part of the name (e.g., mydir-mylib)
|
* @param projectName - unique name. Often directories are part of the name (e.g., mydir-mylib)
|
||||||
|
* @throws If supplied projectName cannot be found
|
||||||
*/
|
*/
|
||||||
export function readProjectConfiguration(
|
export function readProjectConfiguration(
|
||||||
host: Tree,
|
host: Tree,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user