fix(angular): automatically skip remotes not in the current workspace #17473 (#17497)

This commit is contained in:
Colum Ferry 2023-07-05 16:57:40 +01:00 committed by GitHub
parent fc58ef45d6
commit c64f26ef3c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 106 additions and 38 deletions

View File

@ -107,7 +107,7 @@
"skipRemotes": { "skipRemotes": {
"type": "array", "type": "array",
"items": { "type": "string" }, "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": { "pathToManifestFile": {
"type": "string", "type": "string",

View File

@ -72,7 +72,7 @@
"skipRemotes": { "skipRemotes": {
"type": "array", "type": "array",
"items": { "type": "string" }, "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": { "verbose": {
"type": "boolean", "type": "boolean",

View File

@ -18,7 +18,7 @@
"skipRemotes": { "skipRemotes": {
"type": "array", "type": "array",
"items": { "type": "string" }, "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" "x-priority": "important"
}, },
"buildTarget": { "buildTarget": {

View File

@ -34,7 +34,7 @@
"skipRemotes": { "skipRemotes": {
"type": "array", "type": "array",
"items": { "type": "string" }, "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" "x-priority": "important"
}, },
"host": { "host": {

View File

@ -1,5 +1,10 @@
import type { Schema } from './schema'; 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 { scheduleTarget } from 'nx/src/adapter/ngcli-adapter';
import { executeWebpackDevServerBuilder } from '../webpack-dev-server/webpack-dev-server.impl'; import { executeWebpackDevServerBuilder } from '../webpack-dev-server/webpack-dev-server.impl';
import { readProjectsConfigurationFromProjectGraph } from 'nx/src/project-graph/project-graph'; import { readProjectsConfigurationFromProjectGraph } from 'nx/src/project-graph/project-graph';
@ -51,6 +56,12 @@ export function executeModuleFederationDevServerBuilder(
const remotesToSkip = new Set( const remotesToSkip = new Set(
findMatchingProjects(options.skipRemotes, projectGraph.nodes) ?? [] findMatchingProjects(options.skipRemotes, projectGraph.nodes) ?? []
); );
if (remotesToSkip.size > 0) {
logger.info(
`Remotes not served automatically: ${[...remotesToSkip].join(', ')}`
);
}
const staticRemotes = getStaticRemotes( const staticRemotes = getStaticRemotes(
project, project,
context, context,

View File

@ -117,7 +117,7 @@
"items": { "items": {
"type": "string" "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": { "pathToManifestFile": {
"type": "string", "type": "string",

View File

@ -73,7 +73,7 @@
"items": { "items": {
"type": "string" "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": { "verbose": {
"type": "boolean", "type": "boolean",

View File

@ -1,7 +1,7 @@
import { ProjectConfiguration } from 'nx/src/config/workspace-json-project-json'; import { ProjectConfiguration } from 'nx/src/config/workspace-json-project-json';
import { join } from 'path'; import { join } from 'path';
import { existsSync, readFileSync } from 'fs'; import { existsSync, readFileSync } from 'fs';
import { Remotes } from '@nx/devkit'; import { logger, Remotes } from '@nx/devkit';
export function getDynamicRemotes( export function getDynamicRemotes(
project: ProjectConfiguration, project: ProjectConfiguration,
@ -45,19 +45,26 @@ export function getDynamicRemotes(
return []; return [];
} }
const dynamicRemotes = Object.entries(parsedManifest) const allDynamicRemotes = Object.entries(parsedManifest)
.map(([remoteName]) => remoteName) .map(([remoteName]) => remoteName)
.filter((r) => !remotesToSkip.has(r)); .filter((r) => !remotesToSkip.has(r));
const invalidDynamicRemotes = dynamicRemotes.filter(
(remote) => !workspaceProjects[remote] const remotesNotInWorkspace: string[] = [];
);
if (invalidDynamicRemotes.length) { const dynamicRemotes = allDynamicRemotes.filter((remote) => {
throw new Error( if (!workspaceProjects[remote]) {
invalidDynamicRemotes.length === 1 remotesNotInWorkspace.push(remote);
? `Invalid dynamic remote configured in "${pathToManifestFile}": ${invalidDynamicRemotes[0]}.`
: `Invalid dynamic remotes configured in "${pathToManifestFile}": ${invalidDynamicRemotes.join( 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 Array.isArray(mfeConfig.remotes) && mfeConfig.remotes.length > 0
? mfeConfig.remotes ? mfeConfig.remotes
: []; : [];
const staticRemotes = remotesConfig const allStaticRemotes = remotesConfig
.map((remoteDefinition) => .map((remoteDefinition) =>
Array.isArray(remoteDefinition) ? remoteDefinition[0] : remoteDefinition Array.isArray(remoteDefinition) ? remoteDefinition[0] : remoteDefinition
) )
.filter((r) => !remotesToSkip.has(r)); .filter((r) => !remotesToSkip.has(r));
const remotesNotInWorkspace: string[] = [];
const invalidStaticRemotes = staticRemotes.filter( const staticRemotes = allStaticRemotes.filter((remote) => {
(remote) => !workspaceProjects[remote] if (!workspaceProjects[remote]) {
); remotesNotInWorkspace.push(remote);
if (invalidStaticRemotes.length) {
throw new Error( return false;
invalidStaticRemotes.length === 1 }
? `Invalid static remote configured in "${mfConfigPath}": ${invalidStaticRemotes[0]}.` return true;
: `Invalid static remotes configured in "${mfConfigPath}": ${invalidStaticRemotes.join( });
if (remotesNotInWorkspace.length > 0) {
logger.warn(
`Skipping serving ${remotesNotInWorkspace.join(
', ' ', '
)}.` )} as they could not be found in the workspace. Ensure they are served correctly.`
); );
} }

View File

@ -2,8 +2,8 @@ import {
findMatchingProjects, findMatchingProjects,
getMatchingStringsWithCache, getMatchingStringsWithCache,
} from './find-matching-projects'; } from './find-matching-projects';
import minimatch = require('minimatch');
import type { ProjectGraphProjectNode } from '../config/project-graph'; import type { ProjectGraphProjectNode } from '../config/project-graph';
import minimatch = require('minimatch');
describe('findMatchingProjects', () => { describe('findMatchingProjects', () => {
let projectGraph: Record<string, ProjectGraphProjectNode> = { let projectGraph: Record<string, ProjectGraphProjectNode> = {

View File

@ -39,12 +39,39 @@ export default async function* moduleFederationDevServer(
} }
const remotesToSkip = new Set( 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 knownRemotes = (moduleFederationConfig.remotes ?? []).filter((r) => {
const validRemote = Array.isArray(r) ? r[0] : 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( const remotePorts = knownRemotes.map(
(r) => context.projectGraph.nodes[r].data.targets['serve'].options.port (r) => context.projectGraph.nodes[r].data.targets['serve'].options.port
); );

View File

@ -19,7 +19,7 @@
"items": { "items": {
"type": "string" "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" "x-priority": "important"
}, },
"buildTarget": { "buildTarget": {

View File

@ -46,9 +46,27 @@ export default async function* moduleFederationSsrDevServer(
} }
const remotesToSkip = new Set(options.skipRemotes ?? []); const remotesToSkip = new Set(options.skipRemotes ?? []);
const knownRemotes = (moduleFederationConfig.remotes ?? []).filter( const remotesNotInWorkspace: string[] = [];
(r) => !remotesToSkip.has(r) 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 const devServeApps = !options.devRemotes
? [] ? []

View File

@ -35,7 +35,7 @@
"items": { "items": {
"type": "string" "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" "x-priority": "important"
}, },
"host": { "host": {