feat(module-federation): use nx run-many to build static remotes in parallel (#19987)
This commit is contained in:
parent
33ca59673a
commit
1338a7c133
@ -122,6 +122,10 @@
|
|||||||
"description": "Whether the host that is running this executor is the first in the project tree to do so.",
|
"description": "Whether the host that is running this executor is the first in the project tree to do so.",
|
||||||
"default": true,
|
"default": true,
|
||||||
"x-priority": "internal"
|
"x-priority": "internal"
|
||||||
|
},
|
||||||
|
"parallel": {
|
||||||
|
"type": "number",
|
||||||
|
"description": "Max number of parallel processes for building static remotes"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
|
|||||||
@ -100,6 +100,10 @@
|
|||||||
"description": "Whether the host that is running this executor is the first in the project tree to do so.",
|
"description": "Whether the host that is running this executor is the first in the project tree to do so.",
|
||||||
"default": true,
|
"default": true,
|
||||||
"x-priority": "internal"
|
"x-priority": "internal"
|
||||||
|
},
|
||||||
|
"parallel": {
|
||||||
|
"type": "number",
|
||||||
|
"description": "Max number of parallel processes for building static remotes"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"presets": []
|
"presets": []
|
||||||
|
|||||||
@ -121,26 +121,20 @@ export function executeModuleFederationDevServerBuilder(
|
|||||||
pathToManifestFile
|
pathToManifestFile
|
||||||
);
|
);
|
||||||
|
|
||||||
let isCollectingStaticRemoteOutput = true;
|
const staticRemoteBuildPromise = new Promise<void>((res) => {
|
||||||
|
logger.info(
|
||||||
for (const app of remotes.staticRemotes) {
|
`NX Building ${remotes.staticRemotes.length} static remotes...`
|
||||||
const remoteProjectServeTarget =
|
);
|
||||||
projectGraph.nodes[app].data.targets['serve-static'];
|
|
||||||
const isUsingModuleFederationDevServerExecutor =
|
|
||||||
remoteProjectServeTarget.executor.includes(
|
|
||||||
'module-federation-dev-server'
|
|
||||||
);
|
|
||||||
let outWithErr: null | string[] = [];
|
|
||||||
const staticProcess = fork(
|
const staticProcess = fork(
|
||||||
nxBin,
|
nxBin,
|
||||||
[
|
[
|
||||||
'run',
|
'run-many',
|
||||||
`${app}:serve-static${
|
`--target=build`,
|
||||||
context.target.configuration ? `:${context.target.configuration}` : ''
|
`--projects=${remotes.staticRemotes.join(',')}`,
|
||||||
}`,
|
...(context.target.configuration
|
||||||
...(isUsingModuleFederationDevServerExecutor
|
? [`--configuration=${context.target.configuration}`]
|
||||||
? [`--isInitialHost=false`]
|
|
||||||
: []),
|
: []),
|
||||||
|
...(options.parallel ? [`--parallel=${options.parallel}`] : []),
|
||||||
],
|
],
|
||||||
{
|
{
|
||||||
cwd: context.workspaceRoot,
|
cwd: context.workspaceRoot,
|
||||||
@ -148,79 +142,131 @@ export function executeModuleFederationDevServerBuilder(
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
staticProcess.stdout.on('data', (data) => {
|
staticProcess.stdout.on('data', (data) => {
|
||||||
if (isCollectingStaticRemoteOutput) {
|
const ANSII_CODE_REGEX =
|
||||||
outWithErr.push(data.toString());
|
/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g;
|
||||||
} else {
|
const stdoutString = data.toString().replace(ANSII_CODE_REGEX, '');
|
||||||
outWithErr = null;
|
if (stdoutString.includes('Successfully ran target build')) {
|
||||||
staticProcess.stdout.removeAllListeners('data');
|
staticProcess.stdout.removeAllListeners('data');
|
||||||
|
logger.info(`NX Built ${remotes.staticRemotes.length} static remotes`);
|
||||||
|
res();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
staticProcess.stderr.on('data', (data) => logger.info(data.toString()));
|
staticProcess.stderr.on('data', (data) => logger.info(data.toString()));
|
||||||
staticProcess.on('exit', (code) => {
|
staticProcess.on('exit', (code) => {
|
||||||
if (code !== 0) {
|
if (code !== 0) {
|
||||||
logger.info(outWithErr.join(''));
|
throw new Error(`Remotes failed to build. See above for errors.`);
|
||||||
throw new Error(`Remote failed to start. See above for errors.`);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
process.on('SIGTERM', () => staticProcess.kill('SIGTERM'));
|
process.on('SIGTERM', () => staticProcess.kill('SIGTERM'));
|
||||||
process.on('exit', () => staticProcess.kill('SIGTERM'));
|
process.on('exit', () => staticProcess.kill('SIGTERM'));
|
||||||
}
|
});
|
||||||
|
|
||||||
const devRemotes$ = [];
|
return from(staticRemoteBuildPromise).pipe(
|
||||||
for (const app of remotes.devRemotes) {
|
concatMap(() => {
|
||||||
if (!workspaceProjects[app].targets?.['serve']) {
|
let isCollectingStaticRemoteOutput = true;
|
||||||
throw new Error(`Could not find "serve" target in "${app}" project.`);
|
|
||||||
} else if (!workspaceProjects[app].targets?.['serve'].executor) {
|
|
||||||
throw new Error(
|
|
||||||
`Could not find executor for "serve" target in "${app}" project.`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const runOptions: { verbose?: boolean; isInitialHost?: boolean } = {};
|
for (const app of remotes.staticRemotes) {
|
||||||
const [collection, executor] =
|
const remoteProjectServeTarget =
|
||||||
workspaceProjects[app].targets['serve'].executor.split(':');
|
projectGraph.nodes[app].data.targets['serve-static'];
|
||||||
const isUsingModuleFederationDevServerExecutor = executor.includes(
|
const isUsingModuleFederationDevServerExecutor =
|
||||||
'module-federation-dev-server'
|
remoteProjectServeTarget.executor.includes(
|
||||||
);
|
'module-federation-dev-server'
|
||||||
const { schema } = getExecutorInformation(
|
);
|
||||||
collection,
|
let outWithErr: null | string[] = [];
|
||||||
executor,
|
const staticProcess = fork(
|
||||||
workspaceRoot
|
nxBin,
|
||||||
);
|
[
|
||||||
if (
|
'run',
|
||||||
(options.verbose && schema.additionalProperties) ||
|
`${app}:serve-static${
|
||||||
'verbose' in schema.properties
|
context.target.configuration
|
||||||
) {
|
? `:${context.target.configuration}`
|
||||||
runOptions.verbose = options.verbose;
|
: ''
|
||||||
}
|
}`,
|
||||||
|
...(isUsingModuleFederationDevServerExecutor
|
||||||
if (isUsingModuleFederationDevServerExecutor) {
|
? [`--isInitialHost=false`]
|
||||||
runOptions.isInitialHost = false;
|
: []),
|
||||||
}
|
],
|
||||||
|
{
|
||||||
const serve$ = scheduleTarget(
|
cwd: context.workspaceRoot,
|
||||||
context.workspaceRoot,
|
stdio: ['ignore', 'pipe', 'pipe', 'ipc'],
|
||||||
{
|
}
|
||||||
project: app,
|
|
||||||
target: 'serve',
|
|
||||||
configuration: context.target.configuration,
|
|
||||||
runOptions,
|
|
||||||
},
|
|
||||||
options.verbose
|
|
||||||
).then((obs) => {
|
|
||||||
obs.toPromise().catch((err) => {
|
|
||||||
throw new Error(
|
|
||||||
`Remote '${app}' failed to serve correctly due to the following: \r\n${err.toString()}`
|
|
||||||
);
|
);
|
||||||
});
|
staticProcess.stdout.on('data', (data) => {
|
||||||
});
|
if (isCollectingStaticRemoteOutput) {
|
||||||
|
outWithErr.push(data.toString());
|
||||||
|
} else {
|
||||||
|
outWithErr = null;
|
||||||
|
staticProcess.stdout.removeAllListeners('data');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
staticProcess.stderr.on('data', (data) => logger.info(data.toString()));
|
||||||
|
staticProcess.on('exit', (code) => {
|
||||||
|
if (code !== 0) {
|
||||||
|
logger.info(outWithErr.join(''));
|
||||||
|
throw new Error(`Remote failed to start. See above for errors.`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
process.on('SIGTERM', () => staticProcess.kill('SIGTERM'));
|
||||||
|
process.on('exit', () => staticProcess.kill('SIGTERM'));
|
||||||
|
}
|
||||||
|
|
||||||
devRemotes$.push(serve$);
|
const devRemotes$ = [];
|
||||||
}
|
for (const app of remotes.devRemotes) {
|
||||||
|
if (!workspaceProjects[app].targets?.['serve']) {
|
||||||
|
throw new Error(`Could not find "serve" target in "${app}" project.`);
|
||||||
|
} else if (!workspaceProjects[app].targets?.['serve'].executor) {
|
||||||
|
throw new Error(
|
||||||
|
`Could not find executor for "serve" target in "${app}" project.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return devRemotes$.length > 0
|
const runOptions: { verbose?: boolean; isInitialHost?: boolean } = {};
|
||||||
? combineLatest([...devRemotes$]).pipe(concatMap(() => currExecutor))
|
const [collection, executor] =
|
||||||
: currExecutor;
|
workspaceProjects[app].targets['serve'].executor.split(':');
|
||||||
|
const isUsingModuleFederationDevServerExecutor = executor.includes(
|
||||||
|
'module-federation-dev-server'
|
||||||
|
);
|
||||||
|
const { schema } = getExecutorInformation(
|
||||||
|
collection,
|
||||||
|
executor,
|
||||||
|
workspaceRoot
|
||||||
|
);
|
||||||
|
if (
|
||||||
|
(options.verbose && schema.additionalProperties) ||
|
||||||
|
'verbose' in schema.properties
|
||||||
|
) {
|
||||||
|
runOptions.verbose = options.verbose;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isUsingModuleFederationDevServerExecutor) {
|
||||||
|
runOptions.isInitialHost = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const serve$ = scheduleTarget(
|
||||||
|
context.workspaceRoot,
|
||||||
|
{
|
||||||
|
project: app,
|
||||||
|
target: 'serve',
|
||||||
|
configuration: context.target.configuration,
|
||||||
|
runOptions,
|
||||||
|
},
|
||||||
|
options.verbose
|
||||||
|
).then((obs) => {
|
||||||
|
obs.toPromise().catch((err) => {
|
||||||
|
throw new Error(
|
||||||
|
`Remote '${app}' failed to serve correctly due to the following: \r\n${err.toString()}`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
devRemotes$.push(serve$);
|
||||||
|
}
|
||||||
|
|
||||||
|
return devRemotes$.length > 0
|
||||||
|
? combineLatest([...devRemotes$]).pipe(concatMap(() => currExecutor))
|
||||||
|
: currExecutor;
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default require('@angular-devkit/architect').createBuilder(
|
export default require('@angular-devkit/architect').createBuilder(
|
||||||
|
|||||||
@ -22,4 +22,5 @@ export interface Schema {
|
|||||||
pathToManifestFile?: string;
|
pathToManifestFile?: string;
|
||||||
static?: boolean;
|
static?: boolean;
|
||||||
isInitialHost?: boolean;
|
isInitialHost?: boolean;
|
||||||
|
parallel?: number;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -132,6 +132,10 @@
|
|||||||
"description": "Whether the host that is running this executor is the first in the project tree to do so.",
|
"description": "Whether the host that is running this executor is the first in the project tree to do so.",
|
||||||
"default": true,
|
"default": true,
|
||||||
"x-priority": "internal"
|
"x-priority": "internal"
|
||||||
|
},
|
||||||
|
"parallel": {
|
||||||
|
"type": "number",
|
||||||
|
"description": "Max number of parallel processes for building static remotes"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
|
|||||||
@ -24,6 +24,7 @@ type ModuleFederationDevServerOptions = WebDevServerOptions & {
|
|||||||
skipRemotes?: string[];
|
skipRemotes?: string[];
|
||||||
static?: boolean;
|
static?: boolean;
|
||||||
isInitialHost?: boolean;
|
isInitialHost?: boolean;
|
||||||
|
parallel?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
function getBuildOptions(buildTarget: string, context: ExecutorContext) {
|
function getBuildOptions(buildTarget: string, context: ExecutorContext) {
|
||||||
@ -80,6 +81,44 @@ export default async function* moduleFederationDevServer(
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
logger.info(`NX Building ${remotes.staticRemotes.length} static remotes...`);
|
||||||
|
await new Promise<void>((res) => {
|
||||||
|
const staticProcess = fork(
|
||||||
|
nxBin,
|
||||||
|
[
|
||||||
|
'run-many',
|
||||||
|
`--target=build`,
|
||||||
|
`--projects=${remotes.staticRemotes.join(',')}`,
|
||||||
|
...(context.configurationName
|
||||||
|
? [`--configuration=${context.configurationName}`]
|
||||||
|
: []),
|
||||||
|
...(options.parallel ? [`--parallel=${options.parallel}`] : []),
|
||||||
|
],
|
||||||
|
{
|
||||||
|
cwd: context.root,
|
||||||
|
stdio: ['ignore', 'pipe', 'pipe', 'ipc'],
|
||||||
|
}
|
||||||
|
);
|
||||||
|
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, '');
|
||||||
|
if (stdoutString.includes('Successfully ran target build')) {
|
||||||
|
staticProcess.stdout.removeAllListeners('data');
|
||||||
|
logger.info(`NX Built ${remotes.staticRemotes.length} static remotes`);
|
||||||
|
res();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
staticProcess.stderr.on('data', (data) => logger.info(data.toString()));
|
||||||
|
staticProcess.on('exit', (code) => {
|
||||||
|
if (code !== 0) {
|
||||||
|
throw new Error(`Remote failed to start. See above for errors.`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
process.on('SIGTERM', () => staticProcess.kill('SIGTERM'));
|
||||||
|
process.on('exit', () => staticProcess.kill('SIGTERM'));
|
||||||
|
});
|
||||||
|
|
||||||
let isCollectingStaticRemoteOutput = true;
|
let isCollectingStaticRemoteOutput = true;
|
||||||
const devRemoteIters: AsyncIterable<{ success: boolean }>[] = [];
|
const devRemoteIters: AsyncIterable<{ success: boolean }>[] = [];
|
||||||
|
|
||||||
|
|||||||
@ -101,6 +101,10 @@
|
|||||||
"description": "Whether the host that is running this executor is the first in the project tree to do so.",
|
"description": "Whether the host that is running this executor is the first in the project tree to do so.",
|
||||||
"default": true,
|
"default": true,
|
||||||
"x-priority": "internal"
|
"x-priority": "internal"
|
||||||
|
},
|
||||||
|
"parallel": {
|
||||||
|
"type": "number",
|
||||||
|
"description": "Max number of parallel processes for building static remotes"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user