Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 15 additions & 5 deletions packages/nx/src/command-line/daemon/command-object.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,24 @@
import { CommandModule, Argv } from 'yargs';
import { linkToNxDevAndExamples } from '../yargs-utils/documentation';
import { handleErrors } from '../../utils/handle-errors';
import { withVerbose } from '../yargs-utils/shared-options';
import { makeCommandModule } from '../yargs-utils/arguments-of';

export const yargsDaemonCommand: CommandModule = {
const builder = (yargs: Argv) =>
linkToNxDevAndExamples(withVerbose(withDaemonOptions(yargs)), 'daemon');

export const yargsDaemonCommand = makeCommandModule({
command: 'daemon',
describe:
'Prints information about the Nx Daemon process or starts a daemon process.',
builder: (yargs) =>
linkToNxDevAndExamples(withDaemonOptions(yargs), 'daemon'),
handler: async (args) => (await import('./daemon')).daemonHandler(args),
};
builder,
handler: async (args) => {
const exitCode = await handleErrors(args.verbose, async () =>
(await import('./daemon')).daemonHandler(args)
);
process.exit(exitCode);
},
});

function withDaemonOptions(yargs: Argv): Argv {
return yargs
Expand Down
24 changes: 24 additions & 0 deletions packages/nx/src/command-line/yargs-utils/arguments-of.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import type { Argv, CommandModule as YargsCommandModule } from 'yargs';

export type Builder<InitialArgs, FinalArgs> = (
yargs: Argv<InitialArgs>
) => Argv<FinalArgs>;

export type Handler<B extends Builder<any, any>> = (
args: Awaited<ReturnType<B>['argv']>
) => void | Promise<void>;

export interface CommandModule<T, U>
extends Omit<YargsCommandModule<T, U>, 'handler'> {
builder: Builder<T, U>;
handler: Handler<Builder<T, U>>;
}

/**
* Helper function to define a Yargs CommandModule with proper typing and not sacrifice inference.
*/
export function makeCommandModule<T, U>(
module: CommandModule<T, U>
): CommandModule<T, U> {
return module;
}
109 changes: 54 additions & 55 deletions packages/nx/src/project-graph/plugins/get-plugins.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,7 @@ import { isIsolationEnabled } from './isolation/enabled';
*/
let currentPluginsConfigurationHash: string;
let loadedPlugins: LoadedNxPlugin[];
let pendingPluginsPromise:
| Promise<readonly [LoadedNxPlugin[], () => void]>
| undefined;
let pendingPluginsPromise: Promise<LoadedNxPlugin[]> | undefined;
let cleanupSpecifiedPlugins: () => void | undefined;

const loadingMethod = (
Expand All @@ -48,24 +46,16 @@ export async function getPlugins(
return loadedPlugins;
}

// Cleanup current plugins before loading new ones
cleanupSpecifiedPlugins?.();

pendingPluginsPromise ??= loadSpecifiedNxPlugins(pluginsConfiguration, root);

currentPluginsConfigurationHash = pluginsConfigurationHash;
const [[result, cleanupFn], defaultPlugins] = await Promise.all([
pendingPluginsPromise,
const [defaultPlugins, specifiedPlugins] = await Promise.all([
getOnlyDefaultPlugins(root),
(pendingPluginsPromise ??= loadSpecifiedNxPlugins(
pluginsConfiguration,
root
)),
]);

cleanupSpecifiedPlugins = () => {
loadedPlugins = undefined;
pendingPluginsPromise = undefined;
cleanupFn();
};

loadedPlugins = result.concat(defaultPlugins);
loadedPlugins = specifiedPlugins.concat(defaultPlugins);

return loadedPlugins;
}
Expand Down Expand Up @@ -111,6 +101,8 @@ export async function getOnlyDefaultPlugins(root = workspaceRoot) {
export function cleanupPlugins() {
cleanupSpecifiedPlugins?.();
cleanupDefaultPlugins?.();
pendingPluginsPromise = undefined;
pendingDefaultPluginPromise = undefined;
}

/**
Expand Down Expand Up @@ -164,55 +156,62 @@ async function loadDefaultNxPlugins(root = workspaceRoot) {
}

async function loadSpecifiedNxPlugins(
plugins: PluginConfiguration[],
pluginsConfigurations: PluginConfiguration[],
root = workspaceRoot
): Promise<readonly [LoadedNxPlugin[], () => void]> {
): Promise<LoadedNxPlugin[]> {
// Returning existing plugins is handled by getPlugins,
// so, if we are here and there are existing plugins, they are stale
if (cleanupSpecifiedPlugins) {
cleanupSpecifiedPlugins();
}

performance.mark('loadSpecifiedNxPlugins:start');

plugins ??= [];
pluginsConfigurations ??= [];

const cleanupFunctions: Array<() => void> = [];
const ret = [
await Promise.all(
plugins.map(async (plugin, index) => {
const pluginPath = typeof plugin === 'string' ? plugin : plugin.plugin;
performance.mark(`Load Nx Plugin: ${pluginPath} - start`);

const [loadedPluginPromise, cleanup] = await loadingMethod(
plugin,
root,
index
);

cleanupFunctions.push(cleanup);
const res = await loadedPluginPromise;
res.index = index;
performance.mark(`Load Nx Plugin: ${pluginPath} - end`);
performance.measure(
`Load Nx Plugin: ${pluginPath}`,
`Load Nx Plugin: ${pluginPath} - start`,
`Load Nx Plugin: ${pluginPath} - end`
);

return res;
})
),
() => {
for (const fn of cleanupFunctions) {
fn();
}
if (pluginTranspilerIsRegistered()) {
cleanupPluginTSTranspiler();
}
},
] as const;
const plugins = await Promise.all(
pluginsConfigurations.map(async (plugin, index) => {
const pluginPath = typeof plugin === 'string' ? plugin : plugin.plugin;
performance.mark(`Load Nx Plugin: ${pluginPath} - start`);

const [loadedPluginPromise, cleanup] = await loadingMethod(
plugin,
root,
index
);

cleanupFunctions.push(cleanup);
const res = await loadedPluginPromise;
res.index = index;
performance.mark(`Load Nx Plugin: ${pluginPath} - end`);
performance.measure(
`Load Nx Plugin: ${pluginPath}`,
`Load Nx Plugin: ${pluginPath} - start`,
`Load Nx Plugin: ${pluginPath} - end`
);

return res;
})
);
performance.mark('loadSpecifiedNxPlugins:end');
performance.measure(
'loadSpecifiedNxPlugins',
'loadSpecifiedNxPlugins:start',
'loadSpecifiedNxPlugins:end'
);
return ret;

cleanupSpecifiedPlugins = () => {
for (const fn of cleanupFunctions) {
fn();
}
if (pluginTranspilerIsRegistered()) {
cleanupPluginTSTranspiler();
}
pendingPluginsPromise = undefined;
};

return plugins;
}

function getDefaultPlugins(root: string) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ export async function loadRemoteNxPlugin(
const { name, pluginPath, shouldRegisterTSTranspiler } =
await resolveNxPlugin(moduleName, root, getNxRequirePaths(root));

const { worker, socket } = await startPluginWorker();
const { worker, socket } = await startPluginWorker(name);

// Register plugin worker as a subprocess of the main CLI
// This allows metrics collection when the daemon is not used
Expand Down Expand Up @@ -411,7 +411,7 @@ function registerPendingPromise(

global.nxPluginWorkerCount ??= 0;

async function startPluginWorker() {
async function startPluginWorker(name: string) {
// this should only really be true when running unit tests within
// the Nx repo. We still need to start the worker in this case,
// but its typescript.
Expand Down Expand Up @@ -441,6 +441,7 @@ async function startPluginWorker() {
...(isWorkerTypescript ? ['--require', 'ts-node/register'] : []),
workerPath,
ipcPath,
name,
],
{
stdio: 'inherit',
Expand Down
Loading