fix(module-federation): generalize the check of the remote project specified in the buildTarget (#31211)

<!-- Please make sure you have read the submission guidelines before
posting an PR -->
<!--
https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr
-->

<!-- Please make sure that your commit message follows our format -->
<!-- Example: `fix(nx): must begin with lowercase` -->

<!-- If this is a particularly complex change or feature addition, you
can request a dedicated Nx release for this pull request branch. Mention
someone from the Nx team or the `@nrwl/nx-pipelines-reviewers` and they
will confirm if the PR warrants its own release for testing purposes,
and generate it for you if appropriate. -->

## Current Behavior
<!-- This is the behavior we have today -->

## Expected Behavior
<!-- This is the behavior we should expect with the changes in this PR
-->

## Related Issue(s)
<!-- Please link the issue being fixed so it gets closed when this is
merged. -->

Fixes #30808
This commit is contained in:
Jonathan Gelin 2025-05-14 16:00:44 +02:00 committed by GitHub
parent e15e2ed106
commit 3be687bf95
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 64 additions and 40 deletions

View File

@ -1,5 +1,6 @@
import { StartRemoteFn, type StartRemoteIteratorsOptions } from './models';
import {
getBuildTargetNameFromMFDevServer,
getModuleFederationConfig,
getRemotes,
parseStaticRemotesConfig,
@ -29,8 +30,12 @@ export async function startRemoteIterators(
const { projects: workspaceProjects } =
readProjectsConfigurationFromProjectGraph(context.projectGraph);
const project = workspaceProjects[context.projectName];
const buildTargetName = getBuildTargetNameFromMFDevServer(
project,
context.projectGraph
);
const moduleFederationConfig = getModuleFederationConfig(
project.targets.build.options.tsConfig,
project.targets?.[buildTargetName]?.options?.tsConfig,
context.root,
project.root,
pluginName

View File

@ -1,4 +1,10 @@
import { logger, parseTargetString, type ProjectGraph } from '@nx/devkit';
import {
logger,
parseTargetString,
type ProjectConfiguration,
type ProjectGraph,
ProjectGraphProjectNode,
} from '@nx/devkit';
import { registerTsProject } from '@nx/js/src/internal';
import { findMatchingProjects } from 'nx/src/utils/find-matching-projects';
import * as pc from 'picocolors';
@ -46,6 +52,37 @@ function extractRemoteProjectsFromConfig(
return { remotes, dynamicRemotes };
}
// Find the target that uses the module-federation-dev-server executor
export function getBuildTargetNameFromMFDevServer(
projectConfig: ProjectConfiguration,
projectGraph: ProjectGraph
) {
if (projectConfig.targets) {
for (const [targetKey, targetConfig] of Object.entries(
projectConfig.targets
)) {
const executor = targetConfig.executor || '';
// Extract the portion after the `:` in the executor name
const executorParts = executor.split(':');
const executorName =
executorParts.length > 1 ? executorParts[1] : executor;
if (executorName === 'module-federation-dev-server') {
// Extract the buildTarget from the options
if (targetConfig.options?.buildTarget) {
const parsedTarget = parseTargetString(
targetConfig.options.buildTarget,
projectGraph
);
return parsedTarget.target;
}
}
}
}
return 'build';
}
function collectRemoteProjects(
remote: string,
collected: Set<string>,
@ -60,45 +97,13 @@ function collectRemoteProjects(
const remoteProjectRoot = remoteProject.root;
// Find the target that uses the module-federation-dev-server executor
let buildTargetName = 'build';
if (remoteProject.targets) {
for (const [targetKey, targetConfig] of Object.entries(
remoteProject.targets
)) {
const executor = targetConfig.executor || '';
// Extract the portion after the `:` in the executor name
const executorParts = executor.split(':');
const executorName =
executorParts.length > 1 ? executorParts[1] : executor;
if (executorName === 'module-federation-dev-server') {
// Extract the buildTarget from the options
if (targetConfig.options?.buildTarget) {
const parsedTarget = parseTargetString(
targetConfig.options.buildTarget,
const buildTargetName = getBuildTargetNameFromMFDevServer(
remoteProject,
context.projectGraph
);
buildTargetName = parsedTarget.target;
break;
}
}
}
}
let remoteProjectTsConfig =
remoteProject.targets?.[buildTargetName]?.options?.tsConfig ??
[
join(remoteProjectRoot, 'tsconfig.app.json'),
join(remoteProjectRoot, 'tsconfig.json'),
join(context.root, 'tsconfig.json'),
join(context.root, 'tsconfig.base.json'),
].find((p) => existsSync(p));
if (!remoteProjectTsConfig) {
throw new Error(
`Could not find a tsconfig for remote project ${remote}. Please add a tsconfig.app.json or tsconfig.json to the project.`
);
}
remoteProject.targets?.[buildTargetName]?.options?.tsConfig;
const remoteProjectConfig = getModuleFederationConfig(
remoteProjectTsConfig,
context.root,
@ -200,7 +205,7 @@ export function getRemotes(
}
export function getModuleFederationConfig(
tsconfigPath: string,
tsconfigPath: string | undefined,
workspaceRoot: string,
projectRoot: string,
pluginName: 'react' | 'angular' = 'react'
@ -219,6 +224,20 @@ export function getModuleFederationConfig(
let moduleFederationConfigPath = moduleFederationConfigPathJS;
tsconfigPath =
tsconfigPath ??
[
join(projectRoot, 'tsconfig.app.json'),
join(projectRoot, 'tsconfig.json'),
join(workspaceRoot, 'tsconfig.json'),
join(workspaceRoot, 'tsconfig.base.json'),
].find((p) => existsSync(p));
if (!tsconfigPath) {
throw new Error(
`Could not find a tsconfig for remote project located at ${projectRoot}. Please add a tsconfig.app.json or tsconfig.json to the project.`
);
}
// create a no-op so this can be called with issue
const fullTSconfigPath = tsconfigPath.startsWith(workspaceRoot)
? tsconfigPath