diff --git a/docs/healthChecks.md b/docs/healthChecks.md index d47508793..466d7ff84 100644 --- a/docs/healthChecks.md +++ b/docs/healthChecks.md @@ -1,8 +1,8 @@ # Health Check Plugins -Plugins can be used to extend the health checks that `npx react-native doctor` runs. This can be used to add additional checks for out of tree platforms, or other checks that are specific to a community module. +Plugins can be used to extend the health checks that `npx react-native doctor` runs. This can be used to add additional checks for out of tree platforms, or other checks that are specific to a community module. -See [`Plugins`](./plugins.md) for information about how plugins work. +See [`Plugins`](./plugins.md) for information about how plugins work. ## How does it work? @@ -21,7 +21,7 @@ module.exports = { }), runAutomaticFix: async ({loader}) => { await installBar(); - loader.succeed(); + loader.success(); }, }, ], @@ -61,9 +61,7 @@ type HealthCheckInterface = { visible?: boolean | void; isRequired?: boolean; description: string; - getDiagnostics: ( - environmentInfo: EnvironmentInfo, - ) => Promise<{ + getDiagnostics: (environmentInfo: EnvironmentInfo) => Promise<{ version?: string; versions?: [string]; versionRange?: string; @@ -94,7 +92,7 @@ Longer description of this health check ##### `getDiagnostics` -Functions which performs the actual check. Simple checks can just return `needsToBeFixed`. Checks which are looking at versions of an installed component (such as the version of node), can also return `version`, `versions` and `versionRange` to provide better information to be displayed in `react-native doctor` when running the check +Functions which performs the actual check. Simple checks can just return `needsToBeFixed`. Checks which are looking at versions of an installed component (such as the version of node), can also return `version`, `versions` and `versionRange` to provide better information to be displayed in `react-native doctor` when running the check ##### `win32AutomaticFix` @@ -116,7 +114,7 @@ This function will be used to try to fix the issue when `react-native doctor` is ```ts type RunAutomaticFix = (args: { - loader: Ora; + loader: Spinner; logManualInstallation: ({ healthcheck, url, @@ -134,7 +132,7 @@ type RunAutomaticFix = (args: { ##### `loader` -A reference to a [`ora`](https://www.npmjs.com/package/ora) instance which should be used to report success / failure, and progress of the fix. The fix function should always call either `loader.succeed()` or `loader.fail()` before returning. +A reference to a [`nanospinner`](https://www.npmjs.com/package/nanospinner) instance which should be used to report success / failure, and progress of the fix. The fix function should always call either `loader.success()` or `loader.error()` before returning. ##### `logManualInstallation` @@ -146,26 +144,27 @@ Provides information about the current system ### Examples of RunAutomaticFix implementations -A health check that requires the user to manually go download/install something. This check will immediately display a message to notify the user how to fix the issue. +A health check that requires the user to manually go download/install something. This check will immediately display a message to notify the user how to fix the issue. ```ts async function needToInstallFoo({loader, logManualInstallation}) { - loader.fail(); + loader.error(); - return logManualInstallation({ - healthcheck: 'Foo', - url: 'https:/foo.com/download', - }); + return logManualInstallation({ + healthcheck: 'Foo', + url: 'https:/foo.com/download', + }); } ``` -A health check that runs some commands locally which may fix the issue. This check will display a spinner while the exec commands are running. Then once the commands are complete, the spinner will change to a checkmark. +A health check that runs some commands locally which may fix the issue. This check will display a spinner while the exec commands are running. Then once the commands are complete, the spinner will change to a checkmark. ```ts -import { exec } from 'promisify-child-process'; +import {exec} from 'promisify-child-process'; async function fixFoo({loader}) { await exec(`foo --install`); await exec(`foo --fix`); - loader.succeed(); + loader.success(); } +``` diff --git a/docs/init.md b/docs/init.md index 74cb9a7ec..9bf9c1425 100644 --- a/docs/init.md +++ b/docs/init.md @@ -78,25 +78,29 @@ module.exports = { ## Post init script loading -The responsibility of showing the user progress of the "Executing post init script" goes to the implementor. In the cli, the `ora` package is used to display progress. -For a simple usage in a custom template, `ora` can be used like this in a postInitScript : +The responsibility of showing the user progress of the "Executing post init script" goes to the implementor. In the cli, the `nanospinner` package is used to display progress. +For a simple usage in a custom template, `nanospinner` can be used like this in a postInitScript : ```javascript #!/usr/bin/env node -const ora = require('ora'); +const {createSpinner} = require('nanospinner'); -const spinner = ora('Executing post init script '); +const spinner = createSpinner('Executing post init script '); new Promise((resolve) => { spinner.start(); // do something resolve(); -}).then(() => { - spinner.succeed(); -}).catch(() => { - spinner.fail(); - throw new Error('Something went wrong during the post init script execution'); -}); +}) + .then(() => { + spinner.success(); + }) + .catch(() => { + spinner.error(); + throw new Error( + 'Something went wrong during the post init script execution', + ); + }); ``` You can find example custom template [here](https://github.com/Esemesek/react-native-new-template). diff --git a/packages/cli-clean/src/clean.ts b/packages/cli-clean/src/clean.ts index dfc8df2c7..a1dab08da 100644 --- a/packages/cli-clean/src/clean.ts +++ b/packages/cli-clean/src/clean.ts @@ -235,10 +235,10 @@ export async function clean( spinner.start(label); await action() .then(() => { - spinner.succeed(); + spinner.success(); }) .catch((e) => { - spinner.fail(`${label} » ${e}`); + spinner.error(`${label} » ${e}`); }); } } diff --git a/packages/cli-config-apple/package.json b/packages/cli-config-apple/package.json index 4c4ccc7ac..ae14de1a7 100644 --- a/packages/cli-config-apple/package.json +++ b/packages/cli-config-apple/package.json @@ -14,7 +14,7 @@ }, "devDependencies": { "@react-native-community/cli-types": "19.0.0-alpha.0", - "ora": "^5.4.1" + "nanospinner": "^1.0.0" }, "files": [ "build", diff --git a/packages/cli-config-apple/src/tools/installPods.ts b/packages/cli-config-apple/src/tools/installPods.ts index c9d4d1fa4..47df96bf9 100644 --- a/packages/cli-config-apple/src/tools/installPods.ts +++ b/packages/cli-config-apple/src/tools/installPods.ts @@ -1,6 +1,6 @@ import fs from 'fs'; import execa from 'execa'; -import type {Ora} from 'ora'; +import type {Spinner} from 'nanospinner'; import chalk from 'chalk'; import { logger, @@ -22,7 +22,7 @@ interface RunPodInstallOptions { newArchEnabled?: boolean; } -async function runPodInstall(loader: Ora, options: RunPodInstallOptions) { +async function runPodInstall(loader: Spinner, options: RunPodInstallOptions) { const shouldHandleRepoUpdate = options?.shouldHandleRepoUpdate || true; try { loader.start( @@ -56,7 +56,7 @@ async function runPodInstall(loader: Ora, options: RunPodInstallOptions) { newArchEnabled: options?.newArchEnabled, }); } else { - loader.fail(); + loader.error(); logger.error(stderr); throw new CLIError( @@ -70,7 +70,7 @@ async function runPodInstall(loader: Ora, options: RunPodInstallOptions) { } } -async function runPodUpdate(loader: Ora) { +async function runPodUpdate(loader: Spinner) { try { loader.start( `Updating CocoaPods repositories ${chalk.dim( @@ -81,7 +81,7 @@ async function runPodUpdate(loader: Ora) { } catch (error) { // "pod" command outputs errors to stdout (at least some of them) logger.log((error as any).stderr || (error as any).stdout); - loader.fail(); + loader.error(); throw new CLIError( `Failed to update CocoaPods repositories for iOS project.\nPlease try again manually: "pod repo update".\nCocoaPods documentation: ${chalk.dim.underline( @@ -103,7 +103,7 @@ async function installCocoaPodsWithGem() { } } -async function installCocoaPods(loader: Ora) { +async function installCocoaPods(loader: Spinner) { loader.stop(); loader.start('Installing CocoaPods'); @@ -111,9 +111,9 @@ async function installCocoaPods(loader: Ora) { try { await installCocoaPodsWithGem(); - return loader.succeed(); + return loader.success(); } catch (error) { - loader.fail(); + loader.error(); logger.error((error as any).stderr); throw new CLIError( @@ -124,7 +124,7 @@ async function installCocoaPods(loader: Ora) { } } -async function installPods(loader?: Ora, options?: PodInstallOptions) { +async function installPods(loader?: Spinner, options?: PodInstallOptions) { loader = loader || new NoopLoader(); try { if (!options?.iosFolderPath && !fs.existsSync('ios')) { diff --git a/packages/cli-config-apple/src/tools/pods.ts b/packages/cli-config-apple/src/tools/pods.ts index 1ca5693af..9337f9913 100644 --- a/packages/cli-config-apple/src/tools/pods.ts +++ b/packages/cli-config-apple/src/tools/pods.ts @@ -94,7 +94,7 @@ async function install( root: string, reactNativePath: string, ) { - const loader = getLoader('Installing CocoaPods...'); + const loader = getLoader({text: 'Installing CocoaPods...'}); try { await runCodegen({ root, @@ -106,9 +106,9 @@ async function install( iosFolderPath, }); cacheManager.set(packageJson.name, 'dependencies', currentDependenciesHash); - loader.succeed(); + loader.success(); } catch (error) { - loader.fail(); + loader.error(); throw new CLIError( `Something when wrong while installing CocoaPods. Please run ${chalk.bold( 'pod install', @@ -182,7 +182,7 @@ export default async function resolvePods( currentPodfileLockChecksum ?? '', ); } else { - const loader = getLoader('Installing CocoaPods...'); + const loader = getLoader({text: 'Installing CocoaPods...'}); try { await installPods(loader, { skipBundleInstall: !!cachedDependenciesHash, @@ -202,9 +202,9 @@ export default async function resolvePods( 'podfileLock', currentPodfileLockChecksum ?? '', ); - loader.succeed(); + loader.success(); } catch (error) { - loader.fail(); + loader.error(); throw new CLIError( `Something when wrong while installing CocoaPods. Please run ${chalk.bold( 'pod install', diff --git a/packages/cli-config-apple/src/tools/runBundleInstall.ts b/packages/cli-config-apple/src/tools/runBundleInstall.ts index ca46bfa1c..791316a96 100644 --- a/packages/cli-config-apple/src/tools/runBundleInstall.ts +++ b/packages/cli-config-apple/src/tools/runBundleInstall.ts @@ -1,14 +1,14 @@ import execa from 'execa'; import {CLIError, logger, link} from '@react-native-community/cli-tools'; -import type {Ora} from 'ora'; +import type {Spinner} from 'nanospinner'; -async function runBundleInstall(loader: Ora) { +async function runBundleInstall(loader: Spinner) { try { loader.start('Installing Ruby Gems'); await execa('bundle', ['install']); } catch (error) { - loader.fail(); + loader.error(); logger.error((error as any).stderr || (error as any).stdout); throw new CLIError( `Looks like your iOS environment is not properly set. Please go to ${link.docs( @@ -19,7 +19,7 @@ async function runBundleInstall(loader: Ora) { ); } - loader.succeed(); + loader.success(); } export default runBundleInstall; diff --git a/packages/cli-doctor/package.json b/packages/cli-doctor/package.json index 114105e7c..41f7cc76a 100644 --- a/packages/cli-doctor/package.json +++ b/packages/cli-doctor/package.json @@ -18,8 +18,8 @@ "deepmerge": "^4.3.0", "envinfo": "^7.13.0", "execa": "^5.0.0", + "nanospinner": "^1.0.0", "node-stream-zip": "^1.9.1", - "ora": "^5.4.1", "semver": "^7.5.2", "wcwidth": "^1.0.1", "yaml": "^2.2.1" diff --git a/packages/cli-doctor/src/tools/brewInstall.ts b/packages/cli-doctor/src/tools/brewInstall.ts index 3c08ebf51..463f6521c 100644 --- a/packages/cli-doctor/src/tools/brewInstall.ts +++ b/packages/cli-doctor/src/tools/brewInstall.ts @@ -26,7 +26,7 @@ async function brewInstall({ return onSuccess(); } - return loader.succeed(); + return loader.success(); } catch (error) { if (typeof onFail === 'function') { return onFail(); diff --git a/packages/cli-doctor/src/tools/downloadAndUnzip.ts b/packages/cli-doctor/src/tools/downloadAndUnzip.ts index bd4ae20f3..b187f26f6 100644 --- a/packages/cli-doctor/src/tools/downloadAndUnzip.ts +++ b/packages/cli-doctor/src/tools/downloadAndUnzip.ts @@ -24,7 +24,7 @@ export const downloadAndUnzip = async ({ const installer = await fetchToTemp(downloadUrl); - loader.text = `Installing ${component} in "${installPath}"`; + loader.update(`Installing ${component} in "${installPath}"`); try { await unzip(installer, installPath); } finally { diff --git a/packages/cli-doctor/src/tools/healthchecks/__tests__/androidSDK.test.ts b/packages/cli-doctor/src/tools/healthchecks/__tests__/androidSDK.test.ts index 73f25748d..a84d5bbeb 100644 --- a/packages/cli-doctor/src/tools/healthchecks/__tests__/androidSDK.test.ts +++ b/packages/cli-doctor/src/tools/healthchecks/__tests__/androidSDK.test.ts @@ -81,7 +81,7 @@ describe('androidSDK', () => { 'android/build.gradle': ` buildscript { ext { - buildToolsVersion = findProperty('android.buildToolsVersion') ?: '34.0.0' + buildToolsVersion = findProperty('android.buildToolsVersion') ?: '34.0.0' minSdkVersion = 16 compileSdkVersion = 28 targetSdkVersion = 28 @@ -123,8 +123,8 @@ describe('androidSDK', () => { it('installs the SDK if it is missing on Windows', async () => { const loader = new tools.NoopLoader(); - const loaderSucceedSpy = jest.spyOn(loader, 'succeed'); - const loaderFailSpy = jest.spyOn(loader, 'fail'); + const loaderSuccessSpy = jest.spyOn(loader, 'success'); + const loaderErrorSpy = jest.spyOn(loader, 'error'); const downloadAndUnzipSpy = jest .spyOn(downloadAndUnzip, 'downloadAndUnzip') .mockImplementation(() => Promise.resolve()); @@ -178,10 +178,10 @@ describe('androidSDK', () => { expect(requiredComponents.includes(call[0])).toBeTruthy(); } - expect(loaderFailSpy).toHaveBeenCalledTimes(0); + expect(loaderErrorSpy).toHaveBeenCalledTimes(0); expect(logSpy).toHaveBeenCalledTimes(0); - expect(loaderSucceedSpy).toBeCalledWith( + expect(loaderSuccessSpy).toBeCalledWith( 'Android SDK configured. You might need to restart your PC for all changes to take effect.', ); }); diff --git a/packages/cli-doctor/src/tools/healthchecks/__tests__/androidStudio.test.ts b/packages/cli-doctor/src/tools/healthchecks/__tests__/androidStudio.test.ts index 801a44a42..67bc7e388 100644 --- a/packages/cli-doctor/src/tools/healthchecks/__tests__/androidStudio.test.ts +++ b/packages/cli-doctor/src/tools/healthchecks/__tests__/androidStudio.test.ts @@ -53,8 +53,8 @@ describe('androidStudio', () => { it('downloads and unzips Android Studio on Windows when missing', async () => { const loader = new NoopLoader(); - const loaderSucceedSpy = jest.spyOn(loader, 'succeed'); - const loaderFailSpy = jest.spyOn(loader, 'fail'); + const loaderSuccessSpy = jest.spyOn(loader, 'success'); + const loaderErrorSpy = jest.spyOn(loader, 'error'); const downloadAndUnzipSpy = jest .spyOn(downloadAndUnzip, 'downloadAndUnzip') .mockImplementation(() => Promise.resolve()); @@ -65,10 +65,10 @@ describe('androidStudio', () => { environmentInfo, }); - expect(loaderFailSpy).toHaveBeenCalledTimes(0); + expect(loaderErrorSpy).toHaveBeenCalledTimes(0); expect(logSpy).toHaveBeenCalledTimes(0); expect(downloadAndUnzipSpy).toBeCalledTimes(1); - expect(loaderSucceedSpy).toBeCalledWith( + expect(loaderSuccessSpy).toBeCalledWith( `Android Studio installed successfully in "${ downloadAndUnzipSpy.mock.calls[0][0].installPath || '' }".`, diff --git a/packages/cli-doctor/src/tools/healthchecks/__tests__/jdk.test.ts b/packages/cli-doctor/src/tools/healthchecks/__tests__/jdk.test.ts index ae399380d..0355483f8 100644 --- a/packages/cli-doctor/src/tools/healthchecks/__tests__/jdk.test.ts +++ b/packages/cli-doctor/src/tools/healthchecks/__tests__/jdk.test.ts @@ -73,8 +73,8 @@ describe('jdk', () => { it('downloads and unzips JDK on Windows when missing', async () => { const loader = new tools.NoopLoader(); - const loaderSucceedSpy = jest.spyOn(loader, 'succeed'); - const loaderFailSpy = jest.spyOn(loader, 'fail'); + const loaderSuccessSpy = jest.spyOn(loader, 'success'); + const loaderErrorSpy = jest.spyOn(loader, 'error'); const downloadAndUnzipSpy = jest .spyOn(downloadAndUnzip, 'downloadAndUnzip') .mockImplementation(() => Promise.resolve()); @@ -85,10 +85,10 @@ describe('jdk', () => { environmentInfo, }); - expect(loaderFailSpy).toHaveBeenCalledTimes(0); + expect(loaderErrorSpy).toHaveBeenCalledTimes(0); expect(logSpy).toHaveBeenCalledTimes(0); expect(downloadAndUnzipSpy).toBeCalledTimes(1); - expect(loaderSucceedSpy).toBeCalledWith( + expect(loaderSuccessSpy).toBeCalledWith( 'JDK installed successfully. Please restart your shell to see the changes', ); }); diff --git a/packages/cli-doctor/src/tools/healthchecks/__tests__/watchman.test.ts b/packages/cli-doctor/src/tools/healthchecks/__tests__/watchman.test.ts index 82f146aa5..4045522ff 100644 --- a/packages/cli-doctor/src/tools/healthchecks/__tests__/watchman.test.ts +++ b/packages/cli-doctor/src/tools/healthchecks/__tests__/watchman.test.ts @@ -82,12 +82,12 @@ describe('watchman', () => { }); const loaderSpy = new NoopLoader(); - const loaderSucceedSpy = jest.spyOn(loaderSpy, 'succeed'); - const loaderFailSpy = jest.spyOn(loaderSpy, 'fail'); + const loaderSuccessSpy = jest.spyOn(loaderSpy, 'success'); + const loaderErrorSpy = jest.spyOn(loaderSpy, 'error'); const brewInstallSpy = jest .spyOn(brewInstall, 'brewInstall') .mockImplementation(({loader}) => { - loader.succeed(); + loader.success(); return Promise.resolve(); }); @@ -102,9 +102,9 @@ describe('watchman', () => { value: originalPlatform, }); - expect(loaderFailSpy).toHaveBeenCalledTimes(0); + expect(loaderErrorSpy).toHaveBeenCalledTimes(0); expect(logSpy).toHaveBeenCalledTimes(0); expect(brewInstallSpy).toBeCalledTimes(1); - expect(loaderSucceedSpy).toBeCalledTimes(1); + expect(loaderSuccessSpy).toBeCalledTimes(1); }); }); diff --git a/packages/cli-doctor/src/tools/healthchecks/adb.ts b/packages/cli-doctor/src/tools/healthchecks/adb.ts index ff38dbd1b..168211b81 100644 --- a/packages/cli-doctor/src/tools/healthchecks/adb.ts +++ b/packages/cli-doctor/src/tools/healthchecks/adb.ts @@ -38,7 +38,7 @@ export default { } }, runAutomaticFix: async ({loader, logManualInstallation}) => { - loader.fail(); + loader.error(); let hash: string; switch (link.getOS()) { case 'macos': @@ -59,7 +59,7 @@ export default { if (device && device.connected) { tryRunAdbReverse(process.env.RCT_METRO_PORT || 8081, device.deviceId); } - return loader.succeed(); + return loader.success(); } catch (e) { return logManualInstallation({ healthcheck: 'Adb', diff --git a/packages/cli-doctor/src/tools/healthchecks/androidHomeEnvVariable.ts b/packages/cli-doctor/src/tools/healthchecks/androidHomeEnvVariable.ts index 437c85206..651caf770 100644 --- a/packages/cli-doctor/src/tools/healthchecks/androidHomeEnvVariable.ts +++ b/packages/cli-doctor/src/tools/healthchecks/androidHomeEnvVariable.ts @@ -29,12 +29,12 @@ export default { runAutomaticFix: async ({loader, logManualInstallation}) => { // Variable could have been added if installing Android Studio so double checking if (process.env.ANDROID_HOME) { - loader.succeed(); + loader.success(); return; } - loader.fail(); + loader.error(); logManualInstallation({ message, diff --git a/packages/cli-doctor/src/tools/healthchecks/androidNDK.ts b/packages/cli-doctor/src/tools/healthchecks/androidNDK.ts index b3809bc4a..686b2848f 100644 --- a/packages/cli-doctor/src/tools/healthchecks/androidNDK.ts +++ b/packages/cli-doctor/src/tools/healthchecks/androidNDK.ts @@ -25,7 +25,7 @@ export default { const isNDKInstalled = androidSdk !== 'Not Found' && androidSdk['Android NDK'] !== 'Not Found'; - loader.fail(); + loader.error(); if (isNDKInstalled) { return logManualInstallation({ diff --git a/packages/cli-doctor/src/tools/healthchecks/androidSDK.ts b/packages/cli-doctor/src/tools/healthchecks/androidSDK.ts index 60ecafe35..4b6adbcc0 100644 --- a/packages/cli-doctor/src/tools/healthchecks/androidSDK.ts +++ b/packages/cli-doctor/src/tools/healthchecks/androidSDK.ts @@ -109,7 +109,7 @@ export default { const androidSDKRoot = getAndroidSdkRootInstallation(); if (androidSDKRoot === '') { - loader.fail('There was an error finding the Android SDK root'); + loader.error('There was an error finding the Android SDK root'); return; } @@ -122,7 +122,7 @@ export default { }); for (const component of componentsToInstall) { - loader.text = `Installing "${component}" (this may take a few minutes)`; + loader.update(`Installing "${component}" (this may take a few minutes)`); try { await installComponent(component, androidSDKRoot); @@ -131,7 +131,7 @@ export default { } } - loader.text = 'Updating environment variables'; + loader.update('Updating environment variables'); // Required for the emulator to work from the CLI await setEnvironment('ANDROID_SDK_ROOT', androidSDKRoot); @@ -142,8 +142,9 @@ export default { path.join(androidSDKRoot, 'platform-tools'), ); - loader.text = - 'Configuring Hypervisor for faster emulation, this might prompt UAC'; + loader.update( + 'Configuring Hypervisor for faster emulation, this might prompt UAC', + ); const {hypervisor, installed} = await getBestHypervisor(androidSDKRoot); @@ -164,15 +165,15 @@ export default { } } - loader.text = 'Creating AVD'; + loader.update('Creating AVD'); await createAVD(androidSDKRoot, 'pixel_9.0', 'pixel', systemImage); - loader.succeed( + loader.success( 'Android SDK configured. You might need to restart your PC for all changes to take effect.', ); }, runAutomaticFix: async ({loader, logManualInstallation, environmentInfo}) => { - loader.fail(); + loader.error(); if (isSDKInstalled(environmentInfo)) { return logManualInstallation({ diff --git a/packages/cli-doctor/src/tools/healthchecks/androidStudio.ts b/packages/cli-doctor/src/tools/healthchecks/androidStudio.ts index 211578074..994f3b915 100644 --- a/packages/cli-doctor/src/tools/healthchecks/androidStudio.ts +++ b/packages/cli-doctor/src/tools/healthchecks/androidStudio.ts @@ -69,12 +69,12 @@ export default { ico: join(binFolder, 'studio.ico'), }); - loader.succeed( + loader.success( `Android Studio installed successfully in "${installPath}".`, ); }, runAutomaticFix: async ({loader, logManualInstallation}) => { - loader.fail(); + loader.error(); return logManualInstallation({ healthcheck: 'Android Studio', diff --git a/packages/cli-doctor/src/tools/healthchecks/cocoaPods.ts b/packages/cli-doctor/src/tools/healthchecks/cocoaPods.ts index 64443cd4b..7726d4010 100644 --- a/packages/cli-doctor/src/tools/healthchecks/cocoaPods.ts +++ b/packages/cli-doctor/src/tools/healthchecks/cocoaPods.ts @@ -33,13 +33,13 @@ export default { // First attempt to install `cocoapods` await execa('gem', options); - return loader.succeed(loaderSucceedMessage); + return loader.success(loaderSucceedMessage); } catch (_error) { // If that doesn't work then try with sudo try { await runSudo(`gem ${options.join(' ')}`); - return loader.succeed(loaderSucceedMessage); + return loader.success(loaderSucceedMessage); } catch (error) { logError({ healthcheck: label, diff --git a/packages/cli-doctor/src/tools/healthchecks/common.ts b/packages/cli-doctor/src/tools/healthchecks/common.ts index 9bd1647d3..dc46bd425 100644 --- a/packages/cli-doctor/src/tools/healthchecks/common.ts +++ b/packages/cli-doctor/src/tools/healthchecks/common.ts @@ -68,7 +68,7 @@ const logError = ({ command: string; }) => { if (loader) { - loader.fail(); + loader.error(); } addBlankLine(); diff --git a/packages/cli-doctor/src/tools/healthchecks/jdk.ts b/packages/cli-doctor/src/tools/healthchecks/jdk.ts index 358380241..5d5e2efb0 100644 --- a/packages/cli-doctor/src/tools/healthchecks/jdk.ts +++ b/packages/cli-doctor/src/tools/healthchecks/jdk.ts @@ -43,22 +43,22 @@ export default { installPath, }); - loader.text = 'Updating environment variables'; + loader.update('Updating environment variables'); const jdkPath = join(installPath, 'jdk-11.0.2'); await setEnvironment('JAVA_HOME', jdkPath); await updateEnvironment('PATH', join(jdkPath, 'bin')); - loader.succeed( + loader.success( 'JDK installed successfully. Please restart your shell to see the changes', ); } catch (e) { - loader.fail(e as any); + loader.error(e as any); } }, runAutomaticFix: async ({logManualInstallation, loader}) => { - loader.fail(); + loader.error(); logManualInstallation({ healthcheck: 'JDK', url: link.docs('environment-setup', 'android', { diff --git a/packages/cli-doctor/src/tools/healthchecks/nodeJS.ts b/packages/cli-doctor/src/tools/healthchecks/nodeJS.ts index ada2de19c..42a10ec55 100644 --- a/packages/cli-doctor/src/tools/healthchecks/nodeJS.ts +++ b/packages/cli-doctor/src/tools/healthchecks/nodeJS.ts @@ -14,7 +14,7 @@ export default { versionRange: versionRanges.NODE_JS, }), runAutomaticFix: async ({loader, logManualInstallation}) => { - loader.fail(); + loader.error(); logManualInstallation({ healthcheck: 'Node.js', diff --git a/packages/cli-doctor/src/tools/healthchecks/packager.ts b/packages/cli-doctor/src/tools/healthchecks/packager.ts index fa1239adb..5701b3dd3 100644 --- a/packages/cli-doctor/src/tools/healthchecks/packager.ts +++ b/packages/cli-doctor/src/tools/healthchecks/packager.ts @@ -25,7 +25,7 @@ export default { }; }, runAutomaticFix: async ({loader, config}) => { - loader.fail(); + loader.error(); try { const terminal = getDefaultUserTerminal(); const port = Number(process.env.RCT_METRO_PORT) || 8081; @@ -38,7 +38,7 @@ export default { '--terminal', terminal, ]); - return loader.succeed(); + return loader.success(); } return logManualInstallation({ message: diff --git a/packages/cli-doctor/src/tools/healthchecks/ruby.ts b/packages/cli-doctor/src/tools/healthchecks/ruby.ts index 44d023fcb..dc1749867 100644 --- a/packages/cli-doctor/src/tools/healthchecks/ruby.ts +++ b/packages/cli-doctor/src/tools/healthchecks/ruby.ts @@ -170,7 +170,7 @@ export default { return fallbackResult; }, runAutomaticFix: async ({loader, logManualInstallation}) => { - loader.fail(); + loader.error(); logManualInstallation({ healthcheck: 'Ruby', diff --git a/packages/cli-doctor/src/tools/healthchecks/xcode.ts b/packages/cli-doctor/src/tools/healthchecks/xcode.ts index 1fb9ac014..82c0dbfdd 100644 --- a/packages/cli-doctor/src/tools/healthchecks/xcode.ts +++ b/packages/cli-doctor/src/tools/healthchecks/xcode.ts @@ -18,7 +18,7 @@ export default { }; }, runAutomaticFix: async ({loader, logManualInstallation}) => { - loader.fail(); + loader.error(); logManualInstallation({ healthcheck: 'Xcode', diff --git a/packages/cli-doctor/src/tools/healthchecks/xcodeEnv.ts b/packages/cli-doctor/src/tools/healthchecks/xcodeEnv.ts index 2ac85fbca..7b9de18bf 100644 --- a/packages/cli-doctor/src/tools/healthchecks/xcodeEnv.ts +++ b/packages/cli-doctor/src/tools/healthchecks/xcodeEnv.ts @@ -71,9 +71,9 @@ export default { const destFilePath = path.join(pathString, xcodeEnvFile); await copyFileAsync(src, destFilePath); }); - loader.succeed('.xcode.env file have been created!'); + loader.success('.xcode.env file have been created!'); } catch (e) { - loader.fail(e as any); + loader.error(e as any); } }, } as HealthCheckInterface; diff --git a/packages/cli-doctor/src/tools/install.ts b/packages/cli-doctor/src/tools/install.ts index 4845a827a..569a671cc 100644 --- a/packages/cli-doctor/src/tools/install.ts +++ b/packages/cli-doctor/src/tools/install.ts @@ -19,7 +19,7 @@ async function install({pkg, label, url, loader}: InstallArgs) { throw new Error('Not implemented yet'); } } catch (_error) { - loader.fail(); + loader.error(); logManualInstallation({ healthcheck: label, diff --git a/packages/cli-doctor/src/tools/runAutomaticFix.ts b/packages/cli-doctor/src/tools/runAutomaticFix.ts index ec71f4b61..185de0abc 100644 --- a/packages/cli-doctor/src/tools/runAutomaticFix.ts +++ b/packages/cli-doctor/src/tools/runAutomaticFix.ts @@ -80,7 +80,6 @@ export default async function ({ for (const healthcheckToRun of healthchecksToRun) { const spinner = getLoader({ - prefixText: '', text: healthcheckToRun.label, }).start(); diff --git a/packages/cli-doctor/src/types.ts b/packages/cli-doctor/src/types.ts index 63624817a..51755cc15 100644 --- a/packages/cli-doctor/src/types.ts +++ b/packages/cli-doctor/src/types.ts @@ -1,7 +1,7 @@ import type {Config} from '@react-native-community/cli-types'; -import type {Ora} from 'ora'; +import type {Spinner} from 'nanospinner'; -export type Loader = Ora; +export type Loader = Spinner; export type NotFound = 'Not Found'; diff --git a/packages/cli-platform-android/src/commands/runAndroid/listAndroidTasks.ts b/packages/cli-platform-android/src/commands/runAndroid/listAndroidTasks.ts index 983b080b7..3d01a8f17 100644 --- a/packages/cli-platform-android/src/commands/runAndroid/listAndroidTasks.ts +++ b/packages/cli-platform-android/src/commands/runAndroid/listAndroidTasks.ts @@ -38,10 +38,10 @@ export const getGradleTasks = ( const out = execa.sync(cmd, ['tasks', '--group', taskType], { cwd: sourceDir, }).stdout; - loader.succeed(); + loader.success(); return parseTasksFromGradleFile(taskType, out); } catch { - loader.fail(); + loader.error(); return []; } }; diff --git a/packages/cli-tools/package.json b/packages/cli-tools/package.json index 34caa6775..06fb0d34b 100644 --- a/packages/cli-tools/package.json +++ b/packages/cli-tools/package.json @@ -14,7 +14,7 @@ "find-up": "^5.0.0", "launch-editor": "^2.9.1", "mime": "^2.4.1", - "ora": "^5.4.1", + "nanospinner": "^1.0.0", "prompts": "^2.4.2", "semver": "^7.5.2" }, diff --git a/packages/cli-tools/src/loader.ts b/packages/cli-tools/src/loader.ts index f4ef0524d..0c13c9681 100644 --- a/packages/cli-tools/src/loader.ts +++ b/packages/cli-tools/src/loader.ts @@ -1,51 +1,60 @@ -import ora from 'ora'; -import type {Ora, Options, Spinner, Color} from 'ora'; +import {createSpinner} from 'nanospinner'; +import type {Spinner} from 'nanospinner'; import logger from './logger'; -export type Loader = Ora; +export type Loader = Spinner; -class OraNoop implements Loader { - spinner: Spinner = {interval: 1, frames: []}; - indent: number = 0; - isSpinning: boolean = false; - text: string = ''; - prefixText: string = ''; - color: Color = 'blue'; +type Options = Parameters[1]; - succeed(_text?: string | undefined) { +class NanospinnerNoop implements Spinner { + success() { return this; } - fail(_text?: string) { + error() { return this; } - start(_text?: string) { + warn() { + return this; + } + info() { return this; } stop() { return this; } - warn(_text?: string) { + start() { return this; } - info(_text?: string) { + update() { return this; } - stopAndPersist() { + reset() { return this; } clear() { return this; } + spin() { + return this; + } + write() { + return this; + } render() { return this; } - frame() { - return this.text; + loop() { + return this; + } + isSpinning() { + return false; } } -export function getLoader(options?: string | Options | undefined): Loader { - return logger.isVerbose() ? new OraNoop() : ora(options); +export function getLoader(options?: Options): Loader { + return logger.isVerbose() + ? new NanospinnerNoop() + : createSpinner('', options); } -export const NoopLoader = OraNoop; +export const NoopLoader = NanospinnerNoop; diff --git a/packages/cli/src/commands/init/git.ts b/packages/cli/src/commands/init/git.ts index 10eee2411..40fc169ee 100644 --- a/packages/cli/src/commands/init/git.ts +++ b/packages/cli/src/commands/init/git.ts @@ -59,7 +59,7 @@ export const createGitRepository = async (folder: string) => { cwd: folder, }, ); - loader.succeed(); + loader.success(); } catch (e) { loader.stop(); logger.debug( diff --git a/packages/cli/src/commands/init/init.ts b/packages/cli/src/commands/init/init.ts index 87c49f890..eb5e643fe 100644 --- a/packages/cli/src/commands/init/init.ts +++ b/packages/cli/src/commands/init/init.ts @@ -235,7 +235,7 @@ async function createFromTemplate({ yarnConfigOptions, ); - loader.succeed(); + loader.success(); loader.start('Copying template'); const templateName = getTemplateName(templateSourceDir); @@ -246,7 +246,7 @@ async function createFromTemplate({ templateSourceDir, ); - loader.succeed(); + loader.success(); loader.start('Processing template'); await changePlaceholderInTemplate({ @@ -261,7 +261,7 @@ async function createFromTemplate({ await bumpYarnVersion(projectDirectory); } - loader.succeed(); + loader.success(); const {postInitScript} = templateConfig; if (postInitScript) { loader.info('Executing post init script '); @@ -294,7 +294,7 @@ async function createFromTemplate({ reactNativePath, }); await installPods(loader, {}); - loader.succeed(); + loader.success(); setEmptyHashForCachedDependencies(projectName); } else if (installPodsValue === 'undefined') { const {installCocoapods} = await prompt({ @@ -313,7 +313,7 @@ async function createFromTemplate({ reactNativePath, }); await installPods(loader, {}); - loader.succeed(); + loader.success(); setEmptyHashForCachedDependencies(projectName); } } @@ -327,7 +327,7 @@ async function createFromTemplate({ } } else { didInstallPods = false; - loader.succeed('Dependencies installation skipped'); + loader.success('Dependencies installation skipped'); } fs.removeSync(templateSourceDir); @@ -364,7 +364,7 @@ async function installDependencies({ root, }); - loader.succeed(); + loader.success(); } function checkPackageManagerAvailability( diff --git a/yarn.lock b/yarn.lock index b2e69f3bc..7d129e241 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7695,6 +7695,13 @@ nanomatch@^1.2.9: snapdragon "^0.8.1" to-regex "^3.0.1" +nanospinner@^1.0.0: + version "1.2.2" + resolved "https://registry.yarnpkg.com/nanospinner/-/nanospinner-1.2.2.tgz#5a38f4410b5bf7a41585964bee74d32eab3e040b" + integrity sha512-Zt/AmG6qRU3e+WnzGGLuMCEAO/dAu45stNbHY223tUxldaDAeE+FxSPsd9Q+j+paejmm0ZbrNVs5Sraqy3dRxA== + dependencies: + picocolors "^1.1.1" + natural-compare-lite@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz#17b09581988979fddafe0201e931ba933c96cbb4" @@ -8502,6 +8509,11 @@ picocolors@^1.0.0: resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== +picocolors@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" + integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== + picomatch@^2.0.4, picomatch@^2.0.7, picomatch@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" @@ -9701,7 +9713,16 @@ string-natural-compare@^3.0.1: resolved "https://registry.yarnpkg.com/string-natural-compare/-/string-natural-compare-3.0.1.tgz#7a42d58474454963759e8e8b7ae63d71c1e7fdf4" integrity sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw== -"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0": + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -9796,7 +9817,7 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -9817,6 +9838,13 @@ strip-ansi@^5.0.0: dependencies: ansi-regex "^4.1.0" +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + strip-ansi@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.0.1.tgz#61740a08ce36b61e50e65653f07060d000975fb2" @@ -10665,7 +10693,7 @@ wordwrap@^1.0.0: resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" integrity sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus= -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -10683,6 +10711,15 @@ wrap-ansi@^6.0.1, wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"