nx/packages/react/src/utils/build-static.remotes.ts
Colum Ferry 2bc7d4e6e9
feat(react): add module federation static server (#27802)
<!-- 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 -->
We can serve each application in a module federation setup statically,
but only individually.


## Expected Behavior
<!-- This is the behavior we should expect with the changes in this PR
-->
Add a serve static executor for module federation hosts which will also
spin up the remotes statically

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

Fixes #
2024-09-09 12:50:52 -04:00

96 lines
3.3 KiB
TypeScript

import type { StaticRemotesConfig } from '@nx/webpack/src/utils/module-federation/parse-static-remotes-config';
import { ExecutorContext } from '@nx/devkit';
import { ModuleFederationDevServerOptions } from '../executors/module-federation-dev-server/schema';
import { logger } from 'nx/src/utils/logger';
import { fork } from 'node:child_process';
import { join } from 'path';
import { workspaceDataDirectory } from 'nx/src/utils/cache-directory';
import { createWriteStream } from 'fs';
export async function buildStaticRemotes(
staticRemotesConfig: StaticRemotesConfig,
nxBin,
context: ExecutorContext,
options: ModuleFederationDevServerOptions
) {
if (!staticRemotesConfig.remotes.length) {
return;
}
logger.info(
`NX Building ${staticRemotesConfig.remotes.length} static remotes...`
);
const mappedLocationOfRemotes: Record<string, string> = {};
for (const app of staticRemotesConfig.remotes) {
mappedLocationOfRemotes[app] = `http${options.ssl ? 's' : ''}://${
options.host
}:${options.staticRemotesPort}/${
staticRemotesConfig.config[app].urlSegment
}`;
}
await new Promise<void>((res, rej) => {
const staticProcess = fork(
nxBin,
[
'run-many',
`--target=build`,
`--projects=${staticRemotesConfig.remotes.join(',')}`,
...(context.configurationName
? [`--configuration=${context.configurationName}`]
: []),
...(options.parallel ? [`--parallel=${options.parallel}`] : []),
],
{
cwd: context.root,
stdio: ['ignore', 'pipe', 'pipe', 'ipc'],
}
);
// File to debug build failures e.g. 2024-01-01T00_00_0_0Z-build.log'
const remoteBuildLogFile = join(
workspaceDataDirectory,
`${new Date().toISOString().replace(/[:\.]/g, '_')}-build.log`
);
const stdoutStream = createWriteStream(remoteBuildLogFile);
staticProcess.stdout.on('data', (data) => {
const ANSII_CODE_REGEX =
/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g;
const stdoutString = data.toString().replace(ANSII_CODE_REGEX, '');
stdoutStream.write(stdoutString);
// in addition to writing into the stdout stream, also show error directly in console
// so the error is easily discoverable. 'ERROR in' is the key word to search in webpack output.
if (stdoutString.includes('ERROR in')) {
logger.log(stdoutString);
}
if (stdoutString.includes('Successfully ran target build')) {
staticProcess.stdout.removeAllListeners('data');
logger.info(
`NX Built ${staticRemotesConfig.remotes.length} static remotes`
);
res();
}
});
staticProcess.stderr.on('data', (data) => logger.info(data.toString()));
staticProcess.once('exit', (code) => {
stdoutStream.end();
staticProcess.stdout.removeAllListeners('data');
staticProcess.stderr.removeAllListeners('data');
if (code !== 0) {
rej(
`Remote failed to start. A complete log can be found in: ${remoteBuildLogFile}`
);
} else {
res();
}
});
process.on('SIGTERM', () => staticProcess.kill('SIGTERM'));
process.on('exit', () => staticProcess.kill('SIGTERM'));
});
return mappedLocationOfRemotes;
}