diff --git a/docs/generated/packages/angular/executors/module-federation-dev-server.json b/docs/generated/packages/angular/executors/module-federation-dev-server.json index 20bfd91d6b..b0547864c8 100644 --- a/docs/generated/packages/angular/executors/module-federation-dev-server.json +++ b/docs/generated/packages/angular/executors/module-federation-dev-server.json @@ -105,7 +105,20 @@ }, "devRemotes": { "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).", "x-priority": "important" }, @@ -147,7 +160,7 @@ { "required": ["buildTarget"] }, { "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.", "aliases": [], diff --git a/docs/generated/packages/react/executors/module-federation-dev-server.json b/docs/generated/packages/react/executors/module-federation-dev-server.json index 120f8ba404..f605c73383 100644 --- a/docs/generated/packages/react/executors/module-federation-dev-server.json +++ b/docs/generated/packages/react/executors/module-federation-dev-server.json @@ -11,7 +11,20 @@ "properties": { "devRemotes": { "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).", "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." } }, + "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": [] }, "description": "Serve a host or remote application.", diff --git a/packages/angular/docs/module-federation-dev-server-examples.md b/packages/angular/docs/module-federation-dev-server-examples.md index 6a2088a222..ac2163bd5d 100644 --- a/packages/angular/docs/module-federation-dev-server-examples.md +++ b/packages/angular/docs/module-federation-dev-server-examples.md @@ -49,7 +49,13 @@ See an example set up of it below: "options": { "port": 4200, "publicHost": "http://localhost:4200", - "devRemotes": ["remote1", "remote2"] + "devRemotes": [ + "remote1", + { + "remoteName": "remote2", + "configuration": "development" + } + ] } } } diff --git a/packages/angular/src/builders/utilities/module-federation.ts b/packages/angular/src/builders/utilities/module-federation.ts index 29b1ea4568..8a125b041d 100644 --- a/packages/angular/src/builders/utilities/module-federation.ts +++ b/packages/angular/src/builders/utilities/module-federation.ts @@ -155,11 +155,24 @@ export function getStaticRemotes( } export function validateDevRemotes( - options: { devRemotes?: string[] }, + options: { + devRemotes?: ( + | string + | { + remoteName: string; + configuration: string; + } + )[]; + }, workspaceProjects: Record ): void { const invalidDevRemotes = - options.devRemotes?.filter((remote) => !workspaceProjects[remote]) ?? []; + options.devRemotes?.filter( + (remote) => + !(typeof remote === 'string' + ? workspaceProjects[remote] + : workspaceProjects[remote.remoteName]) + ) ?? []; if (invalidDevRemotes.length) { throw new Error( diff --git a/packages/angular/src/executors/module-federation-dev-server/lib/start-dev-remotes.ts b/packages/angular/src/executors/module-federation-dev-server/lib/start-dev-remotes.ts index 3bda86f5ee..321aa6ed7a 100644 --- a/packages/angular/src/executors/module-federation-dev-server/lib/start-dev-remotes.ts +++ b/packages/angular/src/executors/module-federation-dev-server/lib/start-dev-remotes.ts @@ -28,12 +28,21 @@ export async function startRemotes( '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( await runExecutor( { project: app, target, - configuration: context.configurationName, + configuration: configurationOverride ?? context.configurationName, }, { ...(target === 'serve' ? { verbose: options.verbose ?? false } : {}), diff --git a/packages/angular/src/executors/module-federation-dev-server/module-federation-dev-server.impl.ts b/packages/angular/src/executors/module-federation-dev-server/module-federation-dev-server.impl.ts index bd9af8c933..c5f5218ab3 100644 --- a/packages/angular/src/executors/module-federation-dev-server/module-federation-dev-server.impl.ts +++ b/packages/angular/src/executors/module-federation-dev-server/module-federation-dev-server.impl.ts @@ -108,8 +108,12 @@ export async function* moduleFederationDevServerExecutor( 'angular' ); + const remoteNames = options.devRemotes?.map((r) => + typeof r === 'string' ? r : r.remoteName + ); + const remotes = getRemotes( - options.devRemotes, + remoteNames, options.skipRemotes, moduleFederationConfig, { @@ -122,8 +126,10 @@ export async function* moduleFederationDevServerExecutor( if (remotes.devRemotes.length > 0 && !schema.staticRemotesPort) { options.staticRemotesPort = options.devRemotes.reduce((portToUse, r) => { + const remoteName = typeof r === 'string' ? r : r.remoteName; const remotePort = - context.projectGraph.nodes[r].data.targets['serve'].options.port; + context.projectGraph.nodes[remoteName].data.targets['serve'].options + .port; if (remotePort >= portToUse) { return remotePort + 1; } else { diff --git a/packages/angular/src/executors/module-federation-dev-server/schema.d.ts b/packages/angular/src/executors/module-federation-dev-server/schema.d.ts index d976455d9f..3c2d6b0715 100644 --- a/packages/angular/src/executors/module-federation-dev-server/schema.d.ts +++ b/packages/angular/src/executors/module-federation-dev-server/schema.d.ts @@ -16,7 +16,13 @@ interface BaseSchema { hmr?: boolean; watch?: boolean; poll?: number; - devRemotes?: string[]; + devRemotes?: ( + | string + | { + remoteName: string; + configuration: string; + } + )[]; skipRemotes?: string[]; pathToManifestFile?: string; static?: boolean; diff --git a/packages/angular/src/executors/module-federation-dev-server/schema.json b/packages/angular/src/executors/module-federation-dev-server/schema.json index ba1a8e27d8..faffff4561 100644 --- a/packages/angular/src/executors/module-federation-dev-server/schema.json +++ b/packages/angular/src/executors/module-federation-dev-server/schema.json @@ -112,7 +112,24 @@ "devRemotes": { "type": "array", "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).", "x-priority": "important" diff --git a/packages/react/docs/module-federation-dev-server-examples.md b/packages/react/docs/module-federation-dev-server-examples.md new file mode 100644 index 0000000000..1900ab9b93 --- /dev/null +++ b/packages/react/docs/module-federation-dev-server-examples.md @@ -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 %} diff --git a/packages/react/src/executors/module-federation-dev-server/module-federation-dev-server.impl.ts b/packages/react/src/executors/module-federation-dev-server/module-federation-dev-server.impl.ts index 4c6500d6c7..0d9ba683f1 100644 --- a/packages/react/src/executors/module-federation-dev-server/module-federation-dev-server.impl.ts +++ b/packages/react/src/executors/module-federation-dev-server/module-federation-dev-server.impl.ts @@ -26,7 +26,13 @@ import { existsSync } from 'fs'; import { extname } from 'path'; type ModuleFederationDevServerOptions = WebDevServerOptions & { - devRemotes?: string[]; + devRemotes?: ( + | string + | { + remoteName: string; + configuration: string; + } + )[]; skipRemotes?: string[]; static?: boolean; isInitialHost?: boolean; @@ -112,6 +118,15 @@ async function startRemotes( '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 = target === 'serve' ? { @@ -130,7 +145,7 @@ async function startRemotes( { project: app, target, - configuration: context.configurationName, + configuration: configurationOverride ?? context.configurationName, }, overrides, context @@ -307,8 +322,12 @@ export default async function* moduleFederationDevServer( 'react' ); + const remoteNames = options.devRemotes?.map((r) => + typeof r === 'string' ? r : r.remoteName + ); + const remotes = getRemotes( - options.devRemotes, + remoteNames, options.skipRemotes, moduleFederationConfig, { @@ -321,8 +340,10 @@ export default async function* moduleFederationDevServer( if (remotes.devRemotes.length > 0 && !initialStaticRemotesPorts) { options.staticRemotesPort = options.devRemotes.reduce((portToUse, r) => { + const remoteName = typeof r === 'string' ? r : r.remoteName; const remotePort = - context.projectGraph.nodes[r].data.targets['serve'].options.port; + context.projectGraph.nodes[remoteName].data.targets['serve'].options + .port; if (remotePort >= portToUse) { return remotePort + 1; } else { diff --git a/packages/react/src/executors/module-federation-dev-server/schema.json b/packages/react/src/executors/module-federation-dev-server/schema.json index 9e1d49442e..8916f74e77 100644 --- a/packages/react/src/executors/module-federation-dev-server/schema.json +++ b/packages/react/src/executors/module-federation-dev-server/schema.json @@ -9,7 +9,24 @@ "devRemotes": { "type": "array", "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).", "x-priority": "important" @@ -114,5 +131,6 @@ "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." } - } + }, + "examplesFile": "../../../docs/module-federation-dev-server-examples.md" }