diff --git a/packages/@sanity/cli/src/actions/init-project/initProject.ts b/packages/@sanity/cli/src/actions/init-project/initProject.ts index 0b4bbb3ca09..4a5324c882f 100644 --- a/packages/@sanity/cli/src/actions/init-project/initProject.ts +++ b/packages/@sanity/cli/src/actions/init-project/initProject.ts @@ -12,7 +12,6 @@ import pFilter from 'p-filter' import resolveFrom from 'resolve-from' import semver from 'semver' import {evaluate, patch} from 'silver-fleece' -import which from 'which' import {CLIInitStepCompleted} from '../../__telemetry__/init.telemetry' import {type InitFlags} from '../../commands/init/initCommand' @@ -121,7 +120,8 @@ export default async function initSanity( const cliFlags = args.extOptions const unattended = cliFlags.y || cliFlags.yes const print = unattended ? noop : output.print - const warn = (msg: string) => output.warn(chalk.yellow.bgBlack(msg)) + const success = output.success + const warn = output.warn const intendedPlan = cliFlags['project-plan'] const intendedCoupon = cliFlags.coupon @@ -258,25 +258,17 @@ export default async function initSanity( if (hasToken) { trace.log({step: 'login', alreadyLoggedIn: true}) const user = await getUserData(apiClient) - print('') - print( - `${chalk.gray(" 👤 You're logged in as %s using %s")}`, - user.name, - getProviderName(user.provider), - ) - print('') + success('You are logged in as %s using %s', user.email, getProviderName(user.provider)) } else if (!unattended) { trace.log({step: 'login'}) await getOrCreateUser() } - let introMessage = "Let's get you started with a new project" + let introMessage = 'Fetching existing projects' if (cliFlags.quickstart) { - introMessage = "Let's get you started with remote Sanity project" - } else if (remoteTemplateInfo) { - introMessage = "Let's get you started with a remote Sanity template" + introMessage = "Eject your existing project's Sanity configuration" } - print(` ➡️ ${chalk.gray(introMessage)}`) + success(introMessage) print('') const flags = await prepareFlags() @@ -289,7 +281,8 @@ export default async function initSanity( // If user doesn't want to output any template code if (bareOutput) { - print(`\n${chalk.green('Success!')} Below are your project details:\n`) + success('Below are your project details') + print('') print(`Project ID: ${chalk.cyan(projectId)}`) print(`Dataset: ${chalk.cyan(datasetName)}`) print( @@ -659,13 +652,11 @@ export default async function initSanity( context, }) - if (await hasGlobalCli()) { - print('') - print('If you want to delete the imported data, use') - print(` ${chalk.cyan(`sanity dataset delete ${datasetName}`)}`) - print('and create a new clean dataset with') - print(` ${chalk.cyan(`sanity dataset create `)}\n`) - } + print('') + print('If you want to delete the imported data, use') + print(` ${chalk.cyan(`npx sanity dataset delete ${datasetName}`)}`) + print('and create a new clean dataset with') + print(` ${chalk.cyan(`npx sanity dataset create `)}\n`) } const devCommandMap: Record = { @@ -687,12 +678,10 @@ export default async function initSanity( print(`Then: ${chalk.cyan(devCommand)} - to run Sanity Studio\n`) } - if (await hasGlobalCli()) { - print(`Other helpful commands`) - print(`sanity docs - to open the documentation in a browser`) - print(`sanity manage - to open the project settings in a browser`) - print(`sanity help - to explore the CLI manual`) - } + print(`Other helpful commands`) + print(`npx sanity docs - to open the documentation in a browser`) + print(`npx sanity manage - to open the project settings in a browser`) + print(`npx sanity help - to explore the CLI manual`) const sendInvite = isFirstProject && @@ -717,16 +706,13 @@ export default async function initSanity( trace.complete() async function getOrCreateUser() { - print(`We can't find any auth credentials in your Sanity config`) - print('- log in or create a new account\n') + warn('No authentication credentials found in your Sanity config') + print('') // Provide login options (`sanity login`) const {extOptions, ...otherArgs} = args const loginArgs: CliCommandArguments = {...otherArgs, extOptions: {}} await login(loginArgs, {...context, telemetry: trace.newContext('login')}) - - print("Good stuff, you're now authenticated. You'll need a project to keep your") - print('datasets and collaborators safe and snug.') } async function getProjectDetails(): Promise<{ @@ -880,7 +866,7 @@ export default async function initSanity( })) const selected = await prompt.single({ - message: 'Select project to use', + message: 'Create a new project or select an existing one', type: 'list', choices: [ {value: 'new', name: 'Create new project'}, @@ -1059,20 +1045,20 @@ export default async function initSanity( type: 'list', choices: [ { - value: 'moviedb', - name: 'Movie project (schema + sample data)', - }, - { - value: 'shopify', - name: 'E-commerce (Shopify)', + value: 'clean', + name: 'Clean project with no predefined schema types', }, { value: 'blog', name: 'Blog (schema)', }, { - value: 'clean', - name: 'Clean project with no predefined schema types', + value: 'shopify', + name: 'E-commerce (Shopify)', + }, + { + value: 'moviedb', + name: 'Movie project (schema + sample data)', }, ], }) @@ -1560,12 +1546,3 @@ function getImportCommand( !isCommandGroup(cmd) && cmd.name === 'import' && cmd.group === 'dataset', ) } - -async function hasGlobalCli(): Promise { - try { - const globalCliPath = await which('sanity') - return Boolean(globalCliPath) - } catch (err) { - return false - } -} diff --git a/packages/@sanity/cli/src/actions/login/login.ts b/packages/@sanity/cli/src/actions/login/login.ts index 736bcab7eef..2f194a708a9 100644 --- a/packages/@sanity/cli/src/actions/login/login.ts +++ b/packages/@sanity/cli/src/actions/login/login.ts @@ -2,7 +2,6 @@ import http, {type Server} from 'node:http' import os from 'node:os' import {type SanityClient} from '@sanity/client' -import chalk from 'chalk' import open from 'open' import {debug as debugIt} from '../../debug' @@ -146,7 +145,7 @@ export async function login( }) } - output.print(chalk.green('Login successful')) + output.success('Login successful') trace.complete() } @@ -335,7 +334,7 @@ async function promptProviders( const provider = await prompt.single({ type: 'list', - message: 'Login type', + message: 'Please log in or create a new account', choices: providers.map((choice) => choice.title), }) diff --git a/packages/@sanity/cli/src/commands/logout/logoutCommand.ts b/packages/@sanity/cli/src/commands/logout/logoutCommand.ts index 21bf43ca657..e57b41de177 100644 --- a/packages/@sanity/cli/src/commands/logout/logoutCommand.ts +++ b/packages/@sanity/cli/src/commands/logout/logoutCommand.ts @@ -45,7 +45,7 @@ const logoutCommand: CliCommandDefinition = { // Clear cached telemetry consent cfg.delete(TELEMETRY_CONSENT_CONFIG_KEY) - output.print(chalk.green('Logged out')) + output.success('Logged out') }, } diff --git a/packages/@sanity/cli/src/outputters/cliOutputter.ts b/packages/@sanity/cli/src/outputters/cliOutputter.ts index ad399f909bd..08738f36099 100644 --- a/packages/@sanity/cli/src/outputters/cliOutputter.ts +++ b/packages/@sanity/cli/src/outputters/cliOutputter.ts @@ -2,6 +2,10 @@ import chalk from 'chalk' import ora, {type Options, type Ora} from 'ora' +const SYMBOL_CHECK = chalk.green('✓') +const SYMBOL_WARN = chalk.yellow('⚠') +const SYMBOL_FAIL = chalk.red('✗') + let isFirstClear = true export default { @@ -9,15 +13,19 @@ export default { console.log(...args) }, + success(...args: unknown[]): void { + console.log(`${SYMBOL_CHECK} ${args.join(' ')}`) + }, + warn(...args: unknown[]): void { - console.warn(...args) + console.warn(`${SYMBOL_WARN} ${args.join(' ')}`) }, error(...args: unknown[]): void { if (args[0] instanceof Error) { - console.error(chalk.red(args[0].stack)) + console.error(`${SYMBOL_FAIL} ${chalk.red(args[0].stack)}`) } else { - console.error(...args) + console.error(`${SYMBOL_FAIL} ${args.join(' ')}`) } }, @@ -28,7 +36,12 @@ export default { isFirstClear = false }, - spinner(options: Options | string): Ora { - return ora(options) + spinner(options: Options): Ora { + const spinner = ora({...options, spinner: 'dots'}) + // Override the default status methods to use custom symbols instead of emojis + spinner.succeed = (text?: string) => spinner.stopAndPersist({text, symbol: SYMBOL_CHECK}) + spinner.warn = (text?: string) => spinner.stopAndPersist({text, symbol: SYMBOL_WARN}) + spinner.fail = (text?: string) => spinner.stopAndPersist({text, symbol: SYMBOL_FAIL}) + return spinner }, } diff --git a/packages/@sanity/cli/src/types.ts b/packages/@sanity/cli/src/types.ts index 8c419805f8a..33b2c04f9f9 100644 --- a/packages/@sanity/cli/src/types.ts +++ b/packages/@sanity/cli/src/types.ts @@ -150,6 +150,7 @@ export interface CommandRunnerOptions { export interface CliOutputter { print: (...args: unknown[]) => void + success: (...args: unknown[]) => void warn: (...args: unknown[]) => void error: (...args: unknown[]) => void clear: () => void diff --git a/packages/@sanity/cli/src/util/generateCommandsDocumentation.ts b/packages/@sanity/cli/src/util/generateCommandsDocumentation.ts index 9f9b1665f1d..cd85d59066d 100644 --- a/packages/@sanity/cli/src/util/generateCommandsDocumentation.ts +++ b/packages/@sanity/cli/src/util/generateCommandsDocumentation.ts @@ -22,12 +22,15 @@ export function generateCommandsDocumentation( const prefix = group === 'default' ? '' : ` ${group}` const rows = [ - `usage: sanity${prefix} [--default] [-v|--version] [-d|--debug] [-h|--help] []`, + `usage: npx sanity${prefix} [--default] [-v|--version] [-d|--debug] [-h|--help] []`, '', 'Commands:', ] .concat(commands.map((cmd) => ` ${padEnd(cmd.name, cmdLength + 1)} ${cmd.description}`)) - .concat(['', `See 'sanity help${prefix} ' for specific information on a subcommand.`]) + .concat([ + '', + `See 'npx sanity help${prefix} ' for specific information on a subcommand.`, + ]) return rows.join('\n') } @@ -43,14 +46,14 @@ export function generateCommandDocumentation( if (!command) { throw new Error( subCommand - ? `"${subCommand}" is not a subcommand of "${group}". See 'sanity help ${group}'` + ? `"${subCommand}" is not a subcommand of "${group}". See 'npx sanity help ${group}'` : getNoSuchCommandText(group || command), ) } const cmdParts = [group || command.name, subCommand].filter(Boolean).join(' ') return [ - `usage: sanity ${cmdParts} ${command.signature}`, + `usage: npx sanity ${cmdParts} ${command.signature}`, '', ` ${command.description}`, '',