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 82305582cd68a..73ee722d00fb0 100644 --- a/docs/generated/packages/angular/executors/module-federation-dev-server.json +++ b/docs/generated/packages/angular/executors/module-federation-dev-server.json @@ -107,7 +107,7 @@ "skipRemotes": { "type": "array", "items": { "type": "string" }, - "description": "List of remote applications to not automatically serve, either statically or in development mode. This can be useful for multi-repository module federation setups where the host application uses a remote application from an external repository." + "description": "List of remote applications to not automatically serve, either statically or in development mode." }, "pathToManifestFile": { "type": "string", diff --git a/docs/generated/packages/angular/executors/module-federation-dev-ssr.json b/docs/generated/packages/angular/executors/module-federation-dev-ssr.json index 362f71f47eaca..28330f038d446 100644 --- a/docs/generated/packages/angular/executors/module-federation-dev-ssr.json +++ b/docs/generated/packages/angular/executors/module-federation-dev-ssr.json @@ -72,7 +72,7 @@ "skipRemotes": { "type": "array", "items": { "type": "string" }, - "description": "List of remote applications to not automatically serve, either statically or in development mode. This can be useful for multi-repository module federation setups where the host application uses a remote application from an external repository." + "description": "List of remote applications to not automatically serve, either statically or in development mode." }, "verbose": { "type": "boolean", 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 b962cf3a45323..7a346ba1dd993 100644 --- a/docs/generated/packages/react/executors/module-federation-dev-server.json +++ b/docs/generated/packages/react/executors/module-federation-dev-server.json @@ -18,7 +18,7 @@ "skipRemotes": { "type": "array", "items": { "type": "string" }, - "description": "List of remote applications to not automatically serve, either statically or in development mode. This can be useful for multi-repository module federation setups where the host application uses a remote application from an external repository.", + "description": "List of remote applications to not automatically serve, either statically or in development mode.", "x-priority": "important" }, "buildTarget": { diff --git a/docs/generated/packages/react/executors/module-federation-ssr-dev-server.json b/docs/generated/packages/react/executors/module-federation-ssr-dev-server.json index bdca395348390..fb5b3ec88d0c4 100644 --- a/docs/generated/packages/react/executors/module-federation-ssr-dev-server.json +++ b/docs/generated/packages/react/executors/module-federation-ssr-dev-server.json @@ -34,7 +34,7 @@ "skipRemotes": { "type": "array", "items": { "type": "string" }, - "description": "List of remote applications to not automatically serve, either statically or in development mode. This can be useful for multi-repository module federation setups where the host application uses a remote application from an external repository.", + "description": "List of remote applications to not automatically serve, either statically or in development mode.", "x-priority": "important" }, "host": { diff --git a/packages/angular/src/builders/module-federation-dev-server/module-federation-dev-server.impl.ts b/packages/angular/src/builders/module-federation-dev-server/module-federation-dev-server.impl.ts index 9cf8342544535..c622db524c85e 100644 --- a/packages/angular/src/builders/module-federation-dev-server/module-federation-dev-server.impl.ts +++ b/packages/angular/src/builders/module-federation-dev-server/module-federation-dev-server.impl.ts @@ -1,5 +1,10 @@ import type { Schema } from './schema'; -import { readCachedProjectGraph, workspaceRoot, Workspaces } from '@nx/devkit'; +import { + logger, + readCachedProjectGraph, + workspaceRoot, + Workspaces, +} from '@nx/devkit'; import { scheduleTarget } from 'nx/src/adapter/ngcli-adapter'; import { executeWebpackDevServerBuilder } from '../webpack-dev-server/webpack-dev-server.impl'; import { readProjectsConfigurationFromProjectGraph } from 'nx/src/project-graph/project-graph'; @@ -51,6 +56,12 @@ export function executeModuleFederationDevServerBuilder( const remotesToSkip = new Set( findMatchingProjects(options.skipRemotes, projectGraph.nodes) ?? [] ); + + if (remotesToSkip.size > 0) { + logger.info( + `Remotes not served automatically: ${[...remotesToSkip].join(', ')}` + ); + } const staticRemotes = getStaticRemotes( project, context, diff --git a/packages/angular/src/builders/module-federation-dev-server/schema.json b/packages/angular/src/builders/module-federation-dev-server/schema.json index 008903e882c50..897762871c1b6 100644 --- a/packages/angular/src/builders/module-federation-dev-server/schema.json +++ b/packages/angular/src/builders/module-federation-dev-server/schema.json @@ -117,7 +117,7 @@ "items": { "type": "string" }, - "description": "List of remote applications to not automatically serve, either statically or in development mode. This can be useful for multi-repository module federation setups where the host application uses a remote application from an external repository." + "description": "List of remote applications to not automatically serve, either statically or in development mode." }, "pathToManifestFile": { "type": "string", diff --git a/packages/angular/src/builders/module-federation-dev-ssr/schema.json b/packages/angular/src/builders/module-federation-dev-ssr/schema.json index 9687c230e8f20..31dd2e589bec9 100644 --- a/packages/angular/src/builders/module-federation-dev-ssr/schema.json +++ b/packages/angular/src/builders/module-federation-dev-ssr/schema.json @@ -73,7 +73,7 @@ "items": { "type": "string" }, - "description": "List of remote applications to not automatically serve, either statically or in development mode. This can be useful for multi-repository module federation setups where the host application uses a remote application from an external repository." + "description": "List of remote applications to not automatically serve, either statically or in development mode." }, "verbose": { "type": "boolean", diff --git a/packages/angular/src/builders/utilities/module-federation.ts b/packages/angular/src/builders/utilities/module-federation.ts index c7eb08d2d6139..eebc91bc660c1 100644 --- a/packages/angular/src/builders/utilities/module-federation.ts +++ b/packages/angular/src/builders/utilities/module-federation.ts @@ -1,7 +1,7 @@ import { ProjectConfiguration } from 'nx/src/config/workspace-json-project-json'; import { join } from 'path'; import { existsSync, readFileSync } from 'fs'; -import { Remotes } from '@nx/devkit'; +import { logger, Remotes } from '@nx/devkit'; export function getDynamicRemotes( project: ProjectConfiguration, @@ -45,19 +45,26 @@ export function getDynamicRemotes( return []; } - const dynamicRemotes = Object.entries(parsedManifest) + const allDynamicRemotes = Object.entries(parsedManifest) .map(([remoteName]) => remoteName) .filter((r) => !remotesToSkip.has(r)); - const invalidDynamicRemotes = dynamicRemotes.filter( - (remote) => !workspaceProjects[remote] - ); - if (invalidDynamicRemotes.length) { - throw new Error( - invalidDynamicRemotes.length === 1 - ? `Invalid dynamic remote configured in "${pathToManifestFile}": ${invalidDynamicRemotes[0]}.` - : `Invalid dynamic remotes configured in "${pathToManifestFile}": ${invalidDynamicRemotes.join( - ', ' - )}.` + + const remotesNotInWorkspace: string[] = []; + + const dynamicRemotes = allDynamicRemotes.filter((remote) => { + if (!workspaceProjects[remote]) { + remotesNotInWorkspace.push(remote); + + return false; + } + return true; + }); + + if (remotesNotInWorkspace.length > 0) { + logger.warn( + `Skipping serving ${remotesNotInWorkspace.join( + ', ' + )} as they could not be found in the workspace. Ensure they are served correctly.` ); } @@ -89,22 +96,27 @@ export function getStaticRemotes( Array.isArray(mfeConfig.remotes) && mfeConfig.remotes.length > 0 ? mfeConfig.remotes : []; - const staticRemotes = remotesConfig + const allStaticRemotes = remotesConfig .map((remoteDefinition) => Array.isArray(remoteDefinition) ? remoteDefinition[0] : remoteDefinition ) .filter((r) => !remotesToSkip.has(r)); + const remotesNotInWorkspace: string[] = []; - const invalidStaticRemotes = staticRemotes.filter( - (remote) => !workspaceProjects[remote] - ); - if (invalidStaticRemotes.length) { - throw new Error( - invalidStaticRemotes.length === 1 - ? `Invalid static remote configured in "${mfConfigPath}": ${invalidStaticRemotes[0]}.` - : `Invalid static remotes configured in "${mfConfigPath}": ${invalidStaticRemotes.join( - ', ' - )}.` + const staticRemotes = allStaticRemotes.filter((remote) => { + if (!workspaceProjects[remote]) { + remotesNotInWorkspace.push(remote); + + return false; + } + return true; + }); + + if (remotesNotInWorkspace.length > 0) { + logger.warn( + `Skipping serving ${remotesNotInWorkspace.join( + ', ' + )} as they could not be found in the workspace. Ensure they are served correctly.` ); } diff --git a/packages/nx/src/utils/find-matching-projects.spec.ts b/packages/nx/src/utils/find-matching-projects.spec.ts index 0ad750ce5645e..72c2b57bfe7a8 100644 --- a/packages/nx/src/utils/find-matching-projects.spec.ts +++ b/packages/nx/src/utils/find-matching-projects.spec.ts @@ -2,8 +2,8 @@ import { findMatchingProjects, getMatchingStringsWithCache, } from './find-matching-projects'; -import minimatch = require('minimatch'); import type { ProjectGraphProjectNode } from '../config/project-graph'; +import minimatch = require('minimatch'); describe('findMatchingProjects', () => { let projectGraph: Record = { 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 892ebc92b52e6..262ac3d878dd2 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 @@ -39,12 +39,39 @@ export default async function* moduleFederationDevServer( } const remotesToSkip = new Set( - findMatchingProjects(options.skipRemotes ?? [], context.projectGraph.nodes) + findMatchingProjects(options.skipRemotes, context.projectGraph.nodes) ?? [] ); + + if (remotesToSkip.size > 0) { + logger.info( + `Remotes not served automatically: ${[...remotesToSkip.values()].join( + ', ' + )}` + ); + } + const remotesNotInWorkspace: string[] = []; + const knownRemotes = (moduleFederationConfig.remotes ?? []).filter((r) => { const validRemote = Array.isArray(r) ? r[0] : r; - return !remotesToSkip.has(validRemote); + + if (remotesToSkip.has(validRemote)) { + return false; + } else if (!context.projectGraph.nodes[validRemote]) { + remotesNotInWorkspace.push(validRemote); + return false; + } else { + return true; + } }); + + if (remotesNotInWorkspace.length > 0) { + logger.warn( + `Skipping serving ${remotesNotInWorkspace.join( + ', ' + )} as they could not be found in the workspace. Ensure they are served correctly.` + ); + } + const remotePorts = knownRemotes.map( (r) => context.projectGraph.nodes[r].data.targets['serve'].options.port ); 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 261ea0453decb..59cc7679d6d99 100644 --- a/packages/react/src/executors/module-federation-dev-server/schema.json +++ b/packages/react/src/executors/module-federation-dev-server/schema.json @@ -19,7 +19,7 @@ "items": { "type": "string" }, - "description": "List of remote applications to not automatically serve, either statically or in development mode. This can be useful for multi-repository module federation setups where the host application uses a remote application from an external repository.", + "description": "List of remote applications to not automatically serve, either statically or in development mode.", "x-priority": "important" }, "buildTarget": { diff --git a/packages/react/src/executors/module-federation-ssr-dev-server/module-federation-ssr-dev-server.impl.ts b/packages/react/src/executors/module-federation-ssr-dev-server/module-federation-ssr-dev-server.impl.ts index 1e6929ed8feba..e2e2c355a20e3 100644 --- a/packages/react/src/executors/module-federation-ssr-dev-server/module-federation-ssr-dev-server.impl.ts +++ b/packages/react/src/executors/module-federation-ssr-dev-server/module-federation-ssr-dev-server.impl.ts @@ -46,9 +46,27 @@ export default async function* moduleFederationSsrDevServer( } const remotesToSkip = new Set(options.skipRemotes ?? []); - const knownRemotes = (moduleFederationConfig.remotes ?? []).filter( - (r) => !remotesToSkip.has(r) - ); + const remotesNotInWorkspace: string[] = []; + const knownRemotes = (moduleFederationConfig.remotes ?? []).filter((r) => { + const validRemote = Array.isArray(r) ? r[0] : r; + + if (remotesToSkip.has(validRemote)) { + return false; + } else if (!context.projectGraph.nodes[validRemote]) { + remotesNotInWorkspace.push(validRemote); + return false; + } else { + return true; + } + }); + + if (remotesNotInWorkspace.length > 0) { + logger.warn( + `Skipping serving ${remotesNotInWorkspace.join( + ', ' + )} as they could not be found in the workspace. Ensure they are served correctly.` + ); + } const devServeApps = !options.devRemotes ? [] diff --git a/packages/react/src/executors/module-federation-ssr-dev-server/schema.json b/packages/react/src/executors/module-federation-ssr-dev-server/schema.json index 2653fe1c2235d..865ea1ee7cf12 100644 --- a/packages/react/src/executors/module-federation-ssr-dev-server/schema.json +++ b/packages/react/src/executors/module-federation-ssr-dev-server/schema.json @@ -35,7 +35,7 @@ "items": { "type": "string" }, - "description": "List of remote applications to not automatically serve, either statically or in development mode. This can be useful for multi-repository module federation setups where the host application uses a remote application from an external repository.", + "description": "List of remote applications to not automatically serve, either statically or in development mode.", "x-priority": "important" }, "host": {