diff --git a/packages/devkit/src/generators/plugin-migrations/executor-to-plugin-migrator.ts b/packages/devkit/src/generators/plugin-migrations/executor-to-plugin-migrator.ts index 622d0aea9ffde..cb8f4d96ae72c 100644 --- a/packages/devkit/src/generators/plugin-migrations/executor-to-plugin-migrator.ts +++ b/packages/devkit/src/generators/plugin-migrations/executor-to-plugin-migrator.ts @@ -22,6 +22,7 @@ import { ProjectConfigurationsError, mergeTargetConfigurations, retrieveProjectConfigurations, + globalSpinner, } from 'nx/src/devkit-internals'; import type { RunCommandsOptions } from 'nx/src/executors/run-commands/run-commands.impl'; import type { ConfigurationResult } from 'nx/src/project-graph/utils/project-configuration-utils'; @@ -423,6 +424,10 @@ async function migrateProjects( logger?: typeof devkitLogger ): Promise>> { const projects = new Map>(); + const spinner = globalSpinner.start( + `Calculating migration scope...`, + pluginPath + ); for (const migration of migrations) { for (const executor of migration.executors) { @@ -442,9 +447,7 @@ async function migrateProjects( }, logger ); - const result = await migrator.run(); - // invert the result to have a map of projects to their targets for (const [target, projectList] of result.entries()) { for (const project of projectList) { @@ -476,8 +479,10 @@ async function migrateProjects( createNodes, createNodesV2, defaultPluginOptions, - projectGraph + projectGraph, + spinner ); + spinner.succeed(`Migrated configuration for ${projects.size} project(s).\n`); return projects; } @@ -489,7 +494,8 @@ async function addPluginRegistrations( createNodes: CreateNodesV2 | undefined, createNodesV2: CreateNodesV2 | undefined, defaultPluginOptions: T, - projectGraph: ProjectGraph + projectGraph: ProjectGraph, + spinner: typeof globalSpinner ) { const nxJson = readNxJson(tree); @@ -497,7 +503,12 @@ async function addPluginRegistrations( const createNodesResults = new Map(); global.NX_GRAPH_CREATION = true; try { + let index = 0; for (const [project, options] of projects.entries()) { + index++; + spinner.updateText( + `${index}/${projects.size} - Parsing "${project}" configuration...` + ); const projectConfigs = await getCreateNodesResultsForPlugin( tree, { plugin: pluginPath, options }, @@ -537,7 +548,12 @@ async function addPluginRegistrations( return !deepEqual(originalResults, result); }; + let index = 0; for (const [project, options] of projects.entries()) { + index++; + spinner.updateText( + `${index}/${projects.size} - Applying "${project}" configuration...` + ); const existingPlugin = nxJson.plugins?.find( (plugin): plugin is ExpandedPluginConfiguration => typeof plugin !== 'string' && @@ -581,6 +597,7 @@ async function addPluginRegistrations( } } } + spinner.updateText(`Migrations done`); updateNxJson(tree, nxJson); } diff --git a/packages/nx/src/command-line/add/add.ts b/packages/nx/src/command-line/add/add.ts index b2e351bb9be9d..63c4d5eb4c2ba 100644 --- a/packages/nx/src/command-line/add/add.ts +++ b/packages/nx/src/command-line/add/add.ts @@ -20,7 +20,7 @@ import { runPluginInitGenerator, getFailedToInstallPluginErrorMessages, } from '../init/configure-plugins'; -import * as ora from 'ora'; +import { globalSpinner } from '../../utils/spinner'; export function addHandler(options: AddOptions): Promise { return handleErrors(options.verbose, async () => { @@ -43,8 +43,7 @@ async function installPackage( version: string, nxJson: NxJsonConfiguration ): Promise { - const spinner = ora(`Installing ${pkgName}@${version}...`); - spinner.start(); + const spinner = globalSpinner.start(`Installing ${pkgName}@${version}...`); if (existsSync('package.json')) { const pm = detectPackageManager(); @@ -121,8 +120,7 @@ async function initializePlugin( updatePackageScripts = true; } - const spinner = ora(`Initializing ${pkgName}...`); - spinner.start(); + const spinner = globalSpinner.start(`Initializing ${pkgName}...`); try { await runPluginInitGenerator( diff --git a/packages/nx/src/command-line/init/configure-plugins.ts b/packages/nx/src/command-line/init/configure-plugins.ts index 2fd78e0d5d9ed..21e997312f6ba 100644 --- a/packages/nx/src/command-line/init/configure-plugins.ts +++ b/packages/nx/src/command-line/init/configure-plugins.ts @@ -1,4 +1,3 @@ -import * as createSpinner from 'ora'; import { bold } from 'chalk'; import { @@ -22,6 +21,7 @@ import { readNxJson } from '../../config/configuration'; import { nxVersion } from '../../utils/versions'; import { runNxSync } from '../../utils/child-process'; import { writeJsonFile } from '../../utils/fileutils'; +import { globalSpinner } from '../../utils/spinner'; export function installPluginPackages( repoRoot: string, @@ -116,15 +116,14 @@ export async function runPluginInitGenerators( failedPlugins: {}, }; } - const spinner = createSpinner(); let succeededPlugins = []; const failedPlugins: { [pluginName: string]: Error; } = {}; for (const plugin of plugins) { + const spinner = globalSpinner.start('Installing plugin ' + plugin); try { - spinner.start('Installing plugin ' + plugin); await runPluginInitGenerator( plugin, repoRoot, diff --git a/packages/nx/src/command-line/release/version/resolve-current-version.ts b/packages/nx/src/command-line/release/version/resolve-current-version.ts index 7cc994bb7e7e1..eea3bc82efe7c 100644 --- a/packages/nx/src/command-line/release/version/resolve-current-version.ts +++ b/packages/nx/src/command-line/release/version/resolve-current-version.ts @@ -1,5 +1,4 @@ import chalk = require('chalk'); -import * as ora from 'ora'; import { prompt } from 'enquirer'; import { NxReleaseVersionConfiguration } from '../../../config/nx-json'; import type { ProjectGraphProjectNode } from '../../../config/project-graph'; @@ -9,6 +8,7 @@ import { getLatestGitTagForPattern } from '../utils/git'; import { ProjectLogger } from './project-logger'; import type { FinalConfigForProject } from '../utils/release-graph'; import { VersionActions } from './version-actions'; +import { globalSpinner } from '../../../utils/spinner'; export async function resolveCurrentVersion( tree: Tree, @@ -152,11 +152,9 @@ export async function resolveCurrentVersionFromRegistry( let registryTxt = ''; - const spinner = ora( + const spinner = globalSpinner.start( `Resolving the current version for ${projectGraphNode.name} from the configured registry...` ); - spinner.color = 'cyan'; - spinner.start(); try { const res = await versionActions.readCurrentVersionFromRegistry( diff --git a/packages/nx/src/command-line/sync/sync.ts b/packages/nx/src/command-line/sync/sync.ts index 9d6572185951d..eda8d7c33c795 100644 --- a/packages/nx/src/command-line/sync/sync.ts +++ b/packages/nx/src/command-line/sync/sync.ts @@ -1,4 +1,3 @@ -import * as ora from 'ora'; import { readNxJson } from '../../config/nx-json'; import { createProjectGraphAsync } from '../../project-graph/project-graph'; import { output } from '../../utils/output'; @@ -14,6 +13,7 @@ import { } from '../../utils/sync-generators'; import type { SyncArgs } from './command-object'; import chalk = require('chalk'); +import { globalSpinner } from '../../utils/spinner'; interface SyncOptions extends SyncArgs { check?: boolean; @@ -111,8 +111,7 @@ export function syncHandler(options: SyncOptions): Promise { bodyLines: resultBodyLines, }); - const spinner = ora('Syncing the workspace...'); - spinner.start(); + const spinner = globalSpinner.start('Syncing the workspace...'); try { const flushResult = await flushSyncGeneratorChanges(results); diff --git a/packages/nx/src/devkit-internals.ts b/packages/nx/src/devkit-internals.ts index 1c284fae30aaf..a273c107dbc3d 100644 --- a/packages/nx/src/devkit-internals.ts +++ b/packages/nx/src/devkit-internals.ts @@ -43,3 +43,4 @@ export { interpolate } from './tasks-runner/utils'; export { isCI } from './utils/is-ci'; export { isUsingPrettierInTree } from './utils/is-using-prettier'; export { readYamlFile } from './utils/fileutils'; +export { globalSpinner } from './utils/spinner'; diff --git a/packages/nx/src/utils/delayed-spinner.ts b/packages/nx/src/utils/delayed-spinner.ts index 508e6804c1206..f70ed0aec1b25 100644 --- a/packages/nx/src/utils/delayed-spinner.ts +++ b/packages/nx/src/utils/delayed-spinner.ts @@ -1,5 +1,5 @@ -import * as ora from 'ora'; import { isCI } from './is-ci'; +import { globalSpinner, SHOULD_SHOW_SPINNERS } from './spinner'; export type DelayedSpinnerOptions = { delay?: number; @@ -13,7 +13,7 @@ export type DelayedSpinnerOptions = { * takes longer than a certain amount of time. */ export class DelayedSpinner { - spinner: ora.Ora; + spinner: typeof globalSpinner; timeouts: NodeJS.Timeout[] = []; private lastMessage: string; @@ -34,8 +34,8 @@ export class DelayedSpinner { this.lastMessage = message; if (!SHOULD_SHOW_SPINNERS) { console.warn(this.lastMessage); - } else { - this.spinner = ora(this.lastMessage).start(); + } else if (!globalSpinner.isSpinning()) { + this.spinner = globalSpinner.start(this.lastMessage); } }, delay).unref() ); @@ -50,7 +50,7 @@ export class DelayedSpinner { setMessage(message: string) { if (SHOULD_SHOW_SPINNERS) { if (this.spinner) { - this.spinner.text = message; + this.spinner.updateText(message); } } else if (this.ready && this.lastMessage && this.lastMessage !== message) { console.warn(message); @@ -88,8 +88,6 @@ export class DelayedSpinner { } } -const SHOULD_SHOW_SPINNERS = process.stdout.isTTY && !isCI(); - function normalizeDelayedSpinnerOpts( opts: DelayedSpinnerOptions | null | undefined ) { diff --git a/packages/nx/src/utils/spinner.ts b/packages/nx/src/utils/spinner.ts new file mode 100644 index 0000000000000..968ed5abed93a --- /dev/null +++ b/packages/nx/src/utils/spinner.ts @@ -0,0 +1,61 @@ +import * as ora from 'ora'; +import { isCI } from './is-ci'; + +export const SHOULD_SHOW_SPINNERS = process.stdout.isTTY && !isCI(); + +class SpinnerManager { + #ora!: ReturnType; + #prefix: string | undefined; + + start(text?: string, prefix?: string): SpinnerManager { + if (!SHOULD_SHOW_SPINNERS) { + return this; + } + if (prefix !== undefined) { + this.#prefix = prefix; + } + if (this.#ora) { + this.#ora.text = text; + this.#ora.prefixText = this.#prefix; + } else { + this.#createOra(text); + } + this.#ora.start(); + return this; + } + + succeed(text?: string) { + this.#ora?.succeed(text); + } + + stop() { + this.#ora?.stop(); + } + + fail(text?: string) { + this.#ora?.fail(text); + } + + updateText(text?: string) { + if (this.#ora) { + this.#ora.text = text; + } else if (SHOULD_SHOW_SPINNERS) { + this.#createOra(text); + } + } + + isSpinning() { + return this.#ora?.isSpinning ?? false; + } + + #createOra(text?: string) { + this.#ora = ora({ + text: text, + prefixText: this.#prefix, + hideCursor: false, + discardStdin: false, + }); + } +} + +export const globalSpinner = new SpinnerManager();