diff --git a/packages/nx/src/daemon/client/client.ts b/packages/nx/src/daemon/client/client.ts index a41f82862bf67..c8581bd5b7f2e 100644 --- a/packages/nx/src/daemon/client/client.ts +++ b/packages/nx/src/daemon/client/client.ts @@ -77,10 +77,7 @@ import { FLUSH_SYNC_GENERATOR_CHANGES_TO_DISK, type HandleFlushSyncGeneratorChangesToDiskMessage, } from '../message-types/flush-sync-generator-changes-to-disk'; -import { - DelayedSpinner, - SHOULD_SHOW_SPINNERS, -} from '../../utils/delayed-spinner'; +import { DelayedSpinner } from '../../utils/delayed-spinner'; const DAEMON_ENV_SETTINGS = { NX_PROJECT_GLOB_CACHE: 'false', @@ -199,16 +196,13 @@ export class DaemonClient { sourceMaps: ConfigurationSourceMaps; }> { let spinner: DelayedSpinner; - if (SHOULD_SHOW_SPINNERS) { - // If the graph takes a while to load, we want to show a spinner. - spinner = new DelayedSpinner( - 'Calculating the project graph on the Nx Daemon', - 500 - ).scheduleMessageUpdate( - 'Calculating the project graph on the Nx Daemon is taking longer than expected. Re-run with NX_DAEMON=false to see more details.', - 30_000 - ); - } + // If the graph takes a while to load, we want to show a spinner. + spinner = new DelayedSpinner( + 'Calculating the project graph on the Nx Daemon' + ).scheduleMessageUpdate( + 'Calculating the project graph on the Nx Daemon is taking longer than expected. Re-run with NX_DAEMON=false to see more details.', + { ciDelay: 60_000, delay: 30_000 } + ); try { const response = await this.sendToDaemonViaQueue({ type: 'REQUEST_PROJECT_GRAPH', diff --git a/packages/nx/src/project-graph/build-project-graph.ts b/packages/nx/src/project-graph/build-project-graph.ts index a80fc1ed78205..a37283e0e746d 100644 --- a/packages/nx/src/project-graph/build-project-graph.ts +++ b/packages/nx/src/project-graph/build-project-graph.ts @@ -44,7 +44,7 @@ import { ConfigurationSourceMaps, mergeMetadata, } from './utils/project-configuration-utils'; -import { DelayedSpinner, SHOULD_SHOW_SPINNERS } from '../utils/delayed-spinner'; +import { DelayedSpinner } from '../utils/delayed-spinner'; let storedFileMap: FileMap | null = null; let storedAllWorkspaceFiles: FileData[] | null = null; @@ -323,24 +323,28 @@ async function updateProjectGraphWithPlugins( return; } if (inProgressPlugins.size === 1) { - return `Creating project graph dependencies with ${ - inProgressPlugins.keys()[0] - }`; + spinner.setMessage( + `Creating project graph dependencies with ${ + inProgressPlugins.keys()[0] + }` + ); } else if (process.env.NX_VERBOSE_LOGGING === 'true') { - return [ - `Creating project graph dependencies with ${inProgressPlugins.size} plugins`, - ...Array.from(inProgressPlugins).map((p) => ` - ${p}`), - ].join('\n'); + spinner.setMessage( + [ + `Creating project graph dependencies with ${inProgressPlugins.size} plugins`, + ...Array.from(inProgressPlugins).map((p) => ` - ${p}`), + ].join('\n') + ); } else { - return `Creating project graph dependencies with ${inProgressPlugins.size} plugins`; + spinner.setMessage( + `Creating project graph dependencies with ${inProgressPlugins.size} plugins` + ); } } - if (SHOULD_SHOW_SPINNERS) { - spinner = new DelayedSpinner( - `Creating project graph dependencies with ${plugins.length} plugins` - ); - } + spinner = new DelayedSpinner( + `Creating project graph dependencies with ${plugins.length} plugins` + ); await Promise.all( createDependencyPlugins.map(async (plugin) => { @@ -439,22 +443,26 @@ export async function applyProjectMetadata( return; } if (inProgressPlugins.size === 1) { - return `Creating project metadata with ${inProgressPlugins.keys()[0]}`; + spinner.setMessage( + `Creating project metadata with ${inProgressPlugins.keys()[0]}` + ); } else if (process.env.NX_VERBOSE_LOGGING === 'true') { - return [ - `Creating project metadata with ${inProgressPlugins.size} plugins`, - ...Array.from(inProgressPlugins).map((p) => ` - ${p}`), - ].join('\n'); + spinner.setMessage( + [ + `Creating project metadata with ${inProgressPlugins.size} plugins`, + ...Array.from(inProgressPlugins).map((p) => ` - ${p}`), + ].join('\n') + ); } else { - return `Creating project metadata with ${inProgressPlugins.size} plugins`; + spinner.setMessage( + `Creating project metadata with ${inProgressPlugins.size} plugins` + ); } } - if (SHOULD_SHOW_SPINNERS) { - spinner = new DelayedSpinner( - `Creating project metadata with ${plugins.length} plugins` - ); - } + spinner = new DelayedSpinner( + `Creating project metadata with ${plugins.length} plugins` + ); const promises = plugins.map(async (plugin) => { if (plugin.createMetadata) { diff --git a/packages/nx/src/project-graph/utils/project-configuration-utils.ts b/packages/nx/src/project-graph/utils/project-configuration-utils.ts index d36d0a31edb98..f46294ebe928a 100644 --- a/packages/nx/src/project-graph/utils/project-configuration-utils.ts +++ b/packages/nx/src/project-graph/utils/project-configuration-utils.ts @@ -31,11 +31,7 @@ import { } from '../error-types'; import { CreateNodesResult } from '../plugins/public-api'; import { isGlobPattern } from '../../utils/globs'; -import { isOnDaemon } from '../../daemon/is-on-daemon'; -import { - DelayedSpinner, - SHOULD_SHOW_SPINNERS, -} from '../../utils/delayed-spinner'; +import { DelayedSpinner } from '../../utils/delayed-spinner'; export type SourceInformation = [file: string | null, plugin: string]; export type ConfigurationSourceMaps = Record< @@ -339,22 +335,26 @@ export async function createProjectConfigurations( } if (inProgressPlugins.size === 1) { - return `Creating project graph nodes with ${inProgressPlugins.keys()[0]}`; + spinner.setMessage( + `Creating project graph nodes with ${inProgressPlugins.keys()[0]}` + ); } else if (process.env.NX_VERBOSE_LOGGING === 'true') { - return [ - `Creating project graph nodes with ${inProgressPlugins.size} plugins`, - ...Array.from(inProgressPlugins).map((p) => ` - ${p}`), - ].join('\n'); + spinner.setMessage( + [ + `Creating project graph nodes with ${inProgressPlugins.size} plugins`, + ...Array.from(inProgressPlugins).map((p) => ` - ${p}`), + ].join('\n') + ); } else { - return `Creating project graph nodes with ${inProgressPlugins.size} plugins`; + spinner.setMessage( + `Creating project graph nodes with ${inProgressPlugins.size} plugins` + ); } } - if (SHOULD_SHOW_SPINNERS) { - spinner = new DelayedSpinner( - `Creating project graph nodes with ${plugins.length} plugins` - ); - } + spinner = new DelayedSpinner( + `Creating project graph nodes with ${plugins.length} plugins` + ); const results: Array> = []; const errors: Array< diff --git a/packages/nx/src/utils/delayed-spinner.ts b/packages/nx/src/utils/delayed-spinner.ts index 78701d7207b95..7dd5835f51aed 100644 --- a/packages/nx/src/utils/delayed-spinner.ts +++ b/packages/nx/src/utils/delayed-spinner.ts @@ -1,4 +1,10 @@ import * as ora from 'ora'; +import { isCI } from './is-ci'; + +export type DelayedSpinnerOptions = { + delay?: number; + ciDelay?: number; +}; /** * A class that allows to delay the creation of a spinner, as well @@ -10,18 +16,26 @@ export class DelayedSpinner { spinner: ora.Ora; timeouts: NodeJS.Timeout[] = []; initial: number = Date.now(); + lastMessage: string; /** * Constructs a new {@link DelayedSpinner} instance. * - * @param message The message to display in the spinner - * @param ms The number of milliseconds to wait before creating the spinner + * @param opts The options for the spinner */ - constructor(message: string, ms: number = 500) { + constructor(message: string, opts?: DelayedSpinnerOptions) { + opts = normalizeDelayedSpinnerOpts(opts); + const delay = SHOULD_SHOW_SPINNERS ? opts.delay : opts.ciDelay; + this.timeouts.push( setTimeout(() => { - this.spinner = ora(message); - }, ms).unref() + if (!SHOULD_SHOW_SPINNERS) { + console.warn(message); + } else { + this.spinner = ora(message); + } + this.lastMessage = message; + }, delay).unref() ); } @@ -32,7 +46,12 @@ export class DelayedSpinner { * @returns The {@link DelayedSpinner} instance */ setMessage(message: string) { - this.spinner.text = message; + if (this.spinner && SHOULD_SHOW_SPINNERS) { + this.spinner.text = message; + } else if (this.lastMessage && this.lastMessage !== message) { + console.warn(message); + this.lastMessage = message; + } return this; } @@ -40,15 +59,18 @@ export class DelayedSpinner { * Schedules an update to the message of the spinner. Useful for * changing the message after a certain amount of time has passed. * - * @param message The message to display in the spinner - * @param delay How long to wait before updating the message + * @param opts The options for the update * @returns The {@link DelayedSpinner} instance */ - scheduleMessageUpdate(message: string, delay: number) { + scheduleMessageUpdate(message: string, opts?: DelayedSpinnerOptions) { + opts = normalizeDelayedSpinnerOpts(opts); this.timeouts.push( - setTimeout(() => { - this.spinner.text = message; - }, delay).unref() + setTimeout( + () => { + this.setMessage(message); + }, + SHOULD_SHOW_SPINNERS ? opts.delay : opts.ciDelay + ).unref() ); return this; } @@ -62,4 +84,13 @@ export class DelayedSpinner { } } -export const SHOULD_SHOW_SPINNERS = process.stdout.isTTY; +const SHOULD_SHOW_SPINNERS = process.stdout.isTTY && !isCI(); + +function normalizeDelayedSpinnerOpts( + opts: DelayedSpinnerOptions | null | undefined +) { + opts ??= {}; + opts.delay ??= 500; + opts.ciDelay ??= 10_000; + return opts; +}