feat(module-federation): add remote configuration override (#19694)
## Current Behavior The configuration of the served MFE always passed to the remotes. If a new configuration is needed to skip one remote (e.g. `serve:skip-remote1`) but `remote2` then a configuration called `skip-remote1` is needed in the `remote2`. ## Expected Behavior Add an ability to override the configuration and the empty configurations in the remotes can be deleted. ## Related Issue(s) Fixes #19693
This commit is contained in:
parent
0066543096
commit
a08133f440
@ -105,7 +105,20 @@
|
|||||||
},
|
},
|
||||||
"devRemotes": {
|
"devRemotes": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": { "type": "string" },
|
"items": {
|
||||||
|
"oneOf": [
|
||||||
|
{ "type": "string" },
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"remoteName": { "type": "string" },
|
||||||
|
"configuration": { "type": "string" }
|
||||||
|
},
|
||||||
|
"required": ["remoteName"],
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
"description": "List of remote applications to run in development mode (i.e. using serve target).",
|
"description": "List of remote applications to run in development mode (i.e. using serve target).",
|
||||||
"x-priority": "important"
|
"x-priority": "important"
|
||||||
},
|
},
|
||||||
@ -147,7 +160,7 @@
|
|||||||
{ "required": ["buildTarget"] },
|
{ "required": ["buildTarget"] },
|
||||||
{ "required": ["browserTarget"] }
|
{ "required": ["browserTarget"] }
|
||||||
],
|
],
|
||||||
"examplesFile": "## Examples\n\n{% tabs %}\n\n{% tab label=\"Basic Usage\" %}\nThe Module Federation Dev Server will serve a host application and find the remote applications associated with the host and serve them statically also. \nSee an example set up of it below:\n\n```json\n{\n \"serve\": {\n \"executor\": \"@nx/angular:module-federation-dev-server\",\n \"configurations\": {\n \"production\": {\n \"buildTarget\": \"host:build:production\"\n },\n \"development\": {\n \"buildTarget\": \"host:build:development\"\n }\n },\n \"defaultConfiguration\": \"development\",\n \"options\": {\n \"port\": 4200,\n \"publicHost\": \"http://localhost:4200\"\n }\n }\n}\n```\n\n{% /tab %}\n\n{% tab label=\"Serve host with remotes that can be live reloaded\" %}\nThe Module Federation Dev Server will serve a host application and find the remote applications associated with the host and serve a set selection with live reloading enabled also. \nSee an example set up of it below:\n\n```json\n{\n \"serve-with-hmr-remotes\": {\n \"executor\": \"@nx/angular:module-federation-dev-server\",\n \"configurations\": {\n \"production\": {\n \"buildTarget\": \"host:build:production\"\n },\n \"development\": {\n \"buildTarget\": \"host:build:development\"\n }\n },\n \"defaultConfiguration\": \"development\",\n \"options\": {\n \"port\": 4200,\n \"publicHost\": \"http://localhost:4200\",\n \"devRemotes\": [\"remote1\", \"remote2\"]\n }\n }\n}\n```\n\n{% /tab %}\n\n{% /tabs %}\n"
|
"examplesFile": "## Examples\n\n{% tabs %}\n\n{% tab label=\"Basic Usage\" %}\nThe Module Federation Dev Server will serve a host application and find the remote applications associated with the host and serve them statically also. \nSee an example set up of it below:\n\n```json\n{\n \"serve\": {\n \"executor\": \"@nx/angular:module-federation-dev-server\",\n \"configurations\": {\n \"production\": {\n \"buildTarget\": \"host:build:production\"\n },\n \"development\": {\n \"buildTarget\": \"host:build:development\"\n }\n },\n \"defaultConfiguration\": \"development\",\n \"options\": {\n \"port\": 4200,\n \"publicHost\": \"http://localhost:4200\"\n }\n }\n}\n```\n\n{% /tab %}\n\n{% tab label=\"Serve host with remotes that can be live reloaded\" %}\nThe Module Federation Dev Server will serve a host application and find the remote applications associated with the host and serve a set selection with live reloading enabled also. \nSee an example set up of it below:\n\n```json\n{\n \"serve-with-hmr-remotes\": {\n \"executor\": \"@nx/angular:module-federation-dev-server\",\n \"configurations\": {\n \"production\": {\n \"buildTarget\": \"host:build:production\"\n },\n \"development\": {\n \"buildTarget\": \"host:build:development\"\n }\n },\n \"defaultConfiguration\": \"development\",\n \"options\": {\n \"port\": 4200,\n \"publicHost\": \"http://localhost:4200\",\n \"devRemotes\": [\n \"remote1\",\n {\n \"remoteName\": \"remote2\",\n \"configuration\": \"development\"\n }\n ]\n }\n }\n}\n```\n\n{% /tab %}\n\n{% /tabs %}\n"
|
||||||
},
|
},
|
||||||
"description": "Serves host [Module Federation](https://module-federation.io/) applications ([webpack](https://webpack.js.org/)-based) allowing to specify which remote applications should be served with the host.",
|
"description": "Serves host [Module Federation](https://module-federation.io/) applications ([webpack](https://webpack.js.org/)-based) allowing to specify which remote applications should be served with the host.",
|
||||||
"aliases": [],
|
"aliases": [],
|
||||||
|
|||||||
@ -11,7 +11,20 @@
|
|||||||
"properties": {
|
"properties": {
|
||||||
"devRemotes": {
|
"devRemotes": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": { "type": "string" },
|
"items": {
|
||||||
|
"oneOf": [
|
||||||
|
{ "type": "string" },
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"remoteName": { "type": "string" },
|
||||||
|
"configuration": { "type": "string" }
|
||||||
|
},
|
||||||
|
"required": ["remoteName"],
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
"description": "List of remote applications to run in development mode (i.e. using serve target).",
|
"description": "List of remote applications to run in development mode (i.e. using serve target).",
|
||||||
"x-priority": "important"
|
"x-priority": "important"
|
||||||
},
|
},
|
||||||
@ -114,6 +127,7 @@
|
|||||||
"description": "Path to a Module Federation manifest file (e.g. `my/path/to/module-federation.manifest.json`) containing the dynamic remote applications relative to the workspace root."
|
"description": "Path to a Module Federation manifest file (e.g. `my/path/to/module-federation.manifest.json`) containing the dynamic remote applications relative to the workspace root."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"examplesFile": "## Examples\n\n{% tabs %}\n\n{% tab label=\"Basic Usage\" %}\nThe Module Federation Dev Server will serve a host application and find the remote applications associated with the host and serve them statically also. \nSee an example set up of it below:\n\n```json\n{\n \"serve\": {\n \"executor\": \"@nx/react:module-federation-dev-server\",\n \"configurations\": {\n \"production\": {\n \"buildTarget\": \"host:build:production\"\n },\n \"development\": {\n \"buildTarget\": \"host:build:development\"\n }\n },\n \"defaultConfiguration\": \"development\",\n \"options\": {\n \"port\": 4200,\n \"publicHost\": \"http://localhost:4200\"\n }\n }\n}\n```\n\n{% /tab %}\n\n{% tab label=\"Serve host with remotes that can be live reloaded\" %}\nThe Module Federation Dev Server will serve a host application and find the remote applications associated with the host and serve a set selection with live reloading enabled also. \nSee an example set up of it below:\n\n```json\n{\n \"serve-with-hmr-remotes\": {\n \"executor\": \"@nx/react:module-federation-dev-server\",\n \"configurations\": {\n \"production\": {\n \"buildTarget\": \"host:build:production\"\n },\n \"development\": {\n \"buildTarget\": \"host:build:development\"\n }\n },\n \"defaultConfiguration\": \"development\",\n \"options\": {\n \"port\": 4200,\n \"publicHost\": \"http://localhost:4200\",\n \"devRemotes\": [\n \"remote1\",\n {\n \"remoteName\": \"remote2\",\n \"configuration\": \"development\"\n }\n ]\n }\n }\n}\n```\n\n{% /tab %}\n\n{% /tabs %}\n",
|
||||||
"presets": []
|
"presets": []
|
||||||
},
|
},
|
||||||
"description": "Serve a host or remote application.",
|
"description": "Serve a host or remote application.",
|
||||||
|
|||||||
@ -49,7 +49,13 @@ See an example set up of it below:
|
|||||||
"options": {
|
"options": {
|
||||||
"port": 4200,
|
"port": 4200,
|
||||||
"publicHost": "http://localhost:4200",
|
"publicHost": "http://localhost:4200",
|
||||||
"devRemotes": ["remote1", "remote2"]
|
"devRemotes": [
|
||||||
|
"remote1",
|
||||||
|
{
|
||||||
|
"remoteName": "remote2",
|
||||||
|
"configuration": "development"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -155,11 +155,24 @@ export function getStaticRemotes(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function validateDevRemotes(
|
export function validateDevRemotes(
|
||||||
options: { devRemotes?: string[] },
|
options: {
|
||||||
|
devRemotes?: (
|
||||||
|
| string
|
||||||
|
| {
|
||||||
|
remoteName: string;
|
||||||
|
configuration: string;
|
||||||
|
}
|
||||||
|
)[];
|
||||||
|
},
|
||||||
workspaceProjects: Record<string, ProjectConfiguration>
|
workspaceProjects: Record<string, ProjectConfiguration>
|
||||||
): void {
|
): void {
|
||||||
const invalidDevRemotes =
|
const invalidDevRemotes =
|
||||||
options.devRemotes?.filter((remote) => !workspaceProjects[remote]) ?? [];
|
options.devRemotes?.filter(
|
||||||
|
(remote) =>
|
||||||
|
!(typeof remote === 'string'
|
||||||
|
? workspaceProjects[remote]
|
||||||
|
: workspaceProjects[remote.remoteName])
|
||||||
|
) ?? [];
|
||||||
|
|
||||||
if (invalidDevRemotes.length) {
|
if (invalidDevRemotes.length) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
|
|||||||
@ -28,12 +28,21 @@ export async function startRemotes(
|
|||||||
'module-federation-dev-server'
|
'module-federation-dev-server'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const configurationOverride = options.devRemotes.find(
|
||||||
|
(
|
||||||
|
r
|
||||||
|
): r is {
|
||||||
|
remoteName: string;
|
||||||
|
configuration: string;
|
||||||
|
} => typeof r !== 'string' && r.remoteName === app
|
||||||
|
)?.configuration;
|
||||||
|
|
||||||
remoteIters.push(
|
remoteIters.push(
|
||||||
await runExecutor(
|
await runExecutor(
|
||||||
{
|
{
|
||||||
project: app,
|
project: app,
|
||||||
target,
|
target,
|
||||||
configuration: context.configurationName,
|
configuration: configurationOverride ?? context.configurationName,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
...(target === 'serve' ? { verbose: options.verbose ?? false } : {}),
|
...(target === 'serve' ? { verbose: options.verbose ?? false } : {}),
|
||||||
|
|||||||
@ -108,8 +108,12 @@ export async function* moduleFederationDevServerExecutor(
|
|||||||
'angular'
|
'angular'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const remoteNames = options.devRemotes?.map((r) =>
|
||||||
|
typeof r === 'string' ? r : r.remoteName
|
||||||
|
);
|
||||||
|
|
||||||
const remotes = getRemotes(
|
const remotes = getRemotes(
|
||||||
options.devRemotes,
|
remoteNames,
|
||||||
options.skipRemotes,
|
options.skipRemotes,
|
||||||
moduleFederationConfig,
|
moduleFederationConfig,
|
||||||
{
|
{
|
||||||
@ -122,8 +126,10 @@ export async function* moduleFederationDevServerExecutor(
|
|||||||
|
|
||||||
if (remotes.devRemotes.length > 0 && !schema.staticRemotesPort) {
|
if (remotes.devRemotes.length > 0 && !schema.staticRemotesPort) {
|
||||||
options.staticRemotesPort = options.devRemotes.reduce((portToUse, r) => {
|
options.staticRemotesPort = options.devRemotes.reduce((portToUse, r) => {
|
||||||
|
const remoteName = typeof r === 'string' ? r : r.remoteName;
|
||||||
const remotePort =
|
const remotePort =
|
||||||
context.projectGraph.nodes[r].data.targets['serve'].options.port;
|
context.projectGraph.nodes[remoteName].data.targets['serve'].options
|
||||||
|
.port;
|
||||||
if (remotePort >= portToUse) {
|
if (remotePort >= portToUse) {
|
||||||
return remotePort + 1;
|
return remotePort + 1;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -16,7 +16,13 @@ interface BaseSchema {
|
|||||||
hmr?: boolean;
|
hmr?: boolean;
|
||||||
watch?: boolean;
|
watch?: boolean;
|
||||||
poll?: number;
|
poll?: number;
|
||||||
devRemotes?: string[];
|
devRemotes?: (
|
||||||
|
| string
|
||||||
|
| {
|
||||||
|
remoteName: string;
|
||||||
|
configuration: string;
|
||||||
|
}
|
||||||
|
)[];
|
||||||
skipRemotes?: string[];
|
skipRemotes?: string[];
|
||||||
pathToManifestFile?: string;
|
pathToManifestFile?: string;
|
||||||
static?: boolean;
|
static?: boolean;
|
||||||
|
|||||||
@ -112,7 +112,24 @@
|
|||||||
"devRemotes": {
|
"devRemotes": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": {
|
"items": {
|
||||||
"type": "string"
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"remoteName": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"configuration": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["remoteName"],
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"description": "List of remote applications to run in development mode (i.e. using serve target).",
|
"description": "List of remote applications to run in development mode (i.e. using serve target).",
|
||||||
"x-priority": "important"
|
"x-priority": "important"
|
||||||
|
|||||||
66
packages/react/docs/module-federation-dev-server-examples.md
Normal file
66
packages/react/docs/module-federation-dev-server-examples.md
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
## Examples
|
||||||
|
|
||||||
|
{% tabs %}
|
||||||
|
|
||||||
|
{% tab label="Basic Usage" %}
|
||||||
|
The Module Federation Dev Server will serve a host application and find the remote applications associated with the host and serve them statically also.
|
||||||
|
See an example set up of it below:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"serve": {
|
||||||
|
"executor": "@nx/react:module-federation-dev-server",
|
||||||
|
"configurations": {
|
||||||
|
"production": {
|
||||||
|
"buildTarget": "host:build:production"
|
||||||
|
},
|
||||||
|
"development": {
|
||||||
|
"buildTarget": "host:build:development"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"defaultConfiguration": "development",
|
||||||
|
"options": {
|
||||||
|
"port": 4200,
|
||||||
|
"publicHost": "http://localhost:4200"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
{% /tab %}
|
||||||
|
|
||||||
|
{% tab label="Serve host with remotes that can be live reloaded" %}
|
||||||
|
The Module Federation Dev Server will serve a host application and find the remote applications associated with the host and serve a set selection with live reloading enabled also.
|
||||||
|
See an example set up of it below:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"serve-with-hmr-remotes": {
|
||||||
|
"executor": "@nx/react:module-federation-dev-server",
|
||||||
|
"configurations": {
|
||||||
|
"production": {
|
||||||
|
"buildTarget": "host:build:production"
|
||||||
|
},
|
||||||
|
"development": {
|
||||||
|
"buildTarget": "host:build:development"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"defaultConfiguration": "development",
|
||||||
|
"options": {
|
||||||
|
"port": 4200,
|
||||||
|
"publicHost": "http://localhost:4200",
|
||||||
|
"devRemotes": [
|
||||||
|
"remote1",
|
||||||
|
{
|
||||||
|
"remoteName": "remote2",
|
||||||
|
"configuration": "development"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
{% /tab %}
|
||||||
|
|
||||||
|
{% /tabs %}
|
||||||
@ -26,7 +26,13 @@ import { existsSync } from 'fs';
|
|||||||
import { extname } from 'path';
|
import { extname } from 'path';
|
||||||
|
|
||||||
type ModuleFederationDevServerOptions = WebDevServerOptions & {
|
type ModuleFederationDevServerOptions = WebDevServerOptions & {
|
||||||
devRemotes?: string[];
|
devRemotes?: (
|
||||||
|
| string
|
||||||
|
| {
|
||||||
|
remoteName: string;
|
||||||
|
configuration: string;
|
||||||
|
}
|
||||||
|
)[];
|
||||||
skipRemotes?: string[];
|
skipRemotes?: string[];
|
||||||
static?: boolean;
|
static?: boolean;
|
||||||
isInitialHost?: boolean;
|
isInitialHost?: boolean;
|
||||||
@ -112,6 +118,15 @@ async function startRemotes(
|
|||||||
'module-federation-dev-server'
|
'module-federation-dev-server'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const configurationOverride = options.devRemotes?.find(
|
||||||
|
(
|
||||||
|
r
|
||||||
|
): r is {
|
||||||
|
remoteName: string;
|
||||||
|
configuration: string;
|
||||||
|
} => typeof r !== 'string' && r.remoteName === app
|
||||||
|
)?.configuration;
|
||||||
|
|
||||||
const overrides =
|
const overrides =
|
||||||
target === 'serve'
|
target === 'serve'
|
||||||
? {
|
? {
|
||||||
@ -130,7 +145,7 @@ async function startRemotes(
|
|||||||
{
|
{
|
||||||
project: app,
|
project: app,
|
||||||
target,
|
target,
|
||||||
configuration: context.configurationName,
|
configuration: configurationOverride ?? context.configurationName,
|
||||||
},
|
},
|
||||||
overrides,
|
overrides,
|
||||||
context
|
context
|
||||||
@ -307,8 +322,12 @@ export default async function* moduleFederationDevServer(
|
|||||||
'react'
|
'react'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const remoteNames = options.devRemotes?.map((r) =>
|
||||||
|
typeof r === 'string' ? r : r.remoteName
|
||||||
|
);
|
||||||
|
|
||||||
const remotes = getRemotes(
|
const remotes = getRemotes(
|
||||||
options.devRemotes,
|
remoteNames,
|
||||||
options.skipRemotes,
|
options.skipRemotes,
|
||||||
moduleFederationConfig,
|
moduleFederationConfig,
|
||||||
{
|
{
|
||||||
@ -321,8 +340,10 @@ export default async function* moduleFederationDevServer(
|
|||||||
|
|
||||||
if (remotes.devRemotes.length > 0 && !initialStaticRemotesPorts) {
|
if (remotes.devRemotes.length > 0 && !initialStaticRemotesPorts) {
|
||||||
options.staticRemotesPort = options.devRemotes.reduce((portToUse, r) => {
|
options.staticRemotesPort = options.devRemotes.reduce((portToUse, r) => {
|
||||||
|
const remoteName = typeof r === 'string' ? r : r.remoteName;
|
||||||
const remotePort =
|
const remotePort =
|
||||||
context.projectGraph.nodes[r].data.targets['serve'].options.port;
|
context.projectGraph.nodes[remoteName].data.targets['serve'].options
|
||||||
|
.port;
|
||||||
if (remotePort >= portToUse) {
|
if (remotePort >= portToUse) {
|
||||||
return remotePort + 1;
|
return remotePort + 1;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -9,7 +9,24 @@
|
|||||||
"devRemotes": {
|
"devRemotes": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": {
|
"items": {
|
||||||
"type": "string"
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"remoteName": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"configuration": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["remoteName"],
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"description": "List of remote applications to run in development mode (i.e. using serve target).",
|
"description": "List of remote applications to run in development mode (i.e. using serve target).",
|
||||||
"x-priority": "important"
|
"x-priority": "important"
|
||||||
@ -114,5 +131,6 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "Path to a Module Federation manifest file (e.g. `my/path/to/module-federation.manifest.json`) containing the dynamic remote applications relative to the workspace root."
|
"description": "Path to a Module Federation manifest file (e.g. `my/path/to/module-federation.manifest.json`) containing the dynamic remote applications relative to the workspace root."
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
"examplesFile": "../../../docs/module-federation-dev-server-examples.md"
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user