diff --git a/.changeset/plenty-shrimps-protect.md b/.changeset/plenty-shrimps-protect.md new file mode 100644 index 00000000000..478533ff041 --- /dev/null +++ b/.changeset/plenty-shrimps-protect.md @@ -0,0 +1,5 @@ +--- +"@asyncapi/cli": patch +--- + +feat: add ui/ux improvements in config commands diff --git a/src/commands/config/analytics.ts b/src/commands/config/analytics.ts index 3cabf71efeb..24203e5974f 100644 --- a/src/commands/config/analytics.ts +++ b/src/commands/config/analytics.ts @@ -3,6 +3,7 @@ import Command from '../../core/base'; import { promises as fPromises } from 'fs'; import { homedir } from 'os'; import { analyticsFlags } from '../../core/flags/config/analytics.flags'; +import { cyan, red } from 'picocolors'; const { readFile, writeFile } = fPromises; @@ -26,7 +27,7 @@ export default class Analytics extends Command { this.log('\nAnalytics enabled.\n'); this.metricsMetadata.analytics_enabled = flags.enable; } else if (!flags.status) { - this.log('\nPlease append the "--disable" flag to the command in case you prefer to disable analytics, or use the "--enable" flag if you want to enable analytics back again. In case you do not know the analytics current status, then you can append the "--status" flag to be aware of it.\n'); + this.log(`\nPlease append the ${cyan('--disable')} flag to the command if you prefer to disable analytics, or use the ${cyan('--enable')} flag if you want to enable analytics again. To check the current analytics status, use the ${cyan('--status')} flag.\n`); return; } await writeFile(analyticsConfigFile, JSON.stringify(analyticsConfigFileContent), { encoding: 'utf8' }); @@ -35,17 +36,17 @@ export default class Analytics extends Command { if (analyticsConfigFileContent.analyticsEnabled === 'true') { this.log('\nAnalytics are enabled.\n'); } else { - this.log('\nAnalytics are disabled. Please append the "--enable" flag to the command in case you prefer to enable analytics.\n'); + this.log(`\n${red('Analytics are disabled.')} To enable analytics, use the ${cyan('--enable')} flag.\n`); } this.metricsMetadata.analytics_status_checked = flags.status; } } catch (e: any) { switch (e.code) { case 'ENOENT': - this.error(`Unable to access the analytics configuration file. We tried to access the ".asyncapi-analytics" file in in the path "${analyticsConfigFile}" but the file could not be found.`); + this.error(`Unable to access the analytics configuration file. We tried to access the ${cyan('.asyncapi-analytics')} file in the path "${cyan(analyticsConfigFile)}" but the file could not be found.`); break; case 'EEXIST': - this.error(`Unable to update the analytics configuration file. We tried to update your ".asyncapi-analytics" file in the path "${analyticsConfigFile}" but the file does not exist.`); + this.error(`Unable to update the analytics configuration file. We tried to update your ".asyncapi-analytics" file in the path "${cyan(analyticsConfigFile)}" but the file does not exist.`); break; default: this.error(`Unable to change your analytics configuration. Please check the following message for further info about the error:\n\n${e}`); diff --git a/src/commands/config/context/add.ts b/src/commands/config/context/add.ts index f17e19480fd..f48d84f8f88 100644 --- a/src/commands/config/context/add.ts +++ b/src/commands/config/context/add.ts @@ -6,14 +6,15 @@ import { ContextFileWrongFormatError, } from '../../../core/errors/context-error'; import { addFlags } from '../../../core/flags/config/context.flags'; +import { cyan } from 'picocolors'; export default class ContextAdd extends Command { static description = 'Add a context to the store'; static flags = addFlags(); static args = { - 'context-name': Args.string({description: 'context name', required: true}), - 'spec-file-path': Args.string({description: 'file path of the spec file', required: true}), + 'context-name': Args.string({ description: 'context name', required: true }), + 'spec-file-path': Args.string({ description: 'file path of the spec file', required: true }), }; async run() { @@ -24,24 +25,19 @@ export default class ContextAdd extends Command { try { await addContext(contextName, specFilePath); - this.log( - `Added context "${contextName}".\n\nYou can set it as your current context: asyncapi config context use ${contextName}\nYou can use this context when needed by passing ${contextName} as a parameter: asyncapi validate ${contextName}` - ); + this.log(`🎉 Context ${cyan(contextName)} added successfully!`); + this.log(`\nYou can set it as your current context:\n ${cyan('asyncapi')} ${cyan('config')} ${cyan('context')} ${cyan('use')} ${cyan(contextName)}`); + this.log(`\nYou can use this context when needed by passing ${cyan(contextName)} as a parameter:\n ${cyan('asyncapi')} ${cyan('validate')} ${cyan(contextName)}`); if (setAsCurrent) { await setCurrentContext(contextName); - this.log( - `The newly added context "${contextName}", is set as your current context!` - ); + this.log(`\nThe newly added context, ${cyan(contextName)}, is set as your current context!`); } } catch (e) { if ( e instanceof (MissingContextFileError || ContextFileWrongFormatError) ) { - this.log( - 'You have no context file configured. Run "asyncapi config context init" to initialize it.' - ); - return; + this.error(`Unable to add context. You have no context file configured.\nRun ${cyan('asyncapi config context init')} to initialize it.`); } throw e; } diff --git a/src/commands/config/context/current.ts b/src/commands/config/context/current.ts index 49067e33dae..2c0b85ddb89 100644 --- a/src/commands/config/context/current.ts +++ b/src/commands/config/context/current.ts @@ -7,6 +7,7 @@ import { ContextNotFoundError, } from '../../../core/errors/context-error'; import { helpFlag } from '../../../core/flags/global.flags'; +import { cyan } from 'picocolors'; export default class ContextCurrent extends Command { static description = 'Shows the current context that is being used'; @@ -21,27 +22,20 @@ export default class ContextCurrent extends Command { if ( e instanceof (MissingContextFileError || ContextFileWrongFormatError) ) { - this.log( - 'You have no context file configured. Run "asyncapi config context init" to initialize it.' - ); - return; + this.error(`Unable to show current context. You have no context file configured.\nRun ${cyan('asyncapi config context init')} to initialize it.`); } else if (e instanceof ContextFileEmptyError) { - this.log(`Context file "${CONTEXT_FILE_PATH}" is empty.`); - return; + this.error(`Context file ${cyan(CONTEXT_FILE_PATH)} is empty.`); } else if ( e instanceof ContextNotFoundError || (fileContent && !fileContent.current) ) { - this.log( - 'No context is set as current. Run "asyncapi config context" to see all available options.' - ); - return; + this.error(`No context is set as current.\nRun ${cyan('asyncapi config context')} to see all available options.`); } throw e; } if (fileContent) { - this.log(`${fileContent.current}: ${fileContent.context}`); + this.log(`${cyan(fileContent.current)}: ${fileContent.context}`); } } } diff --git a/src/commands/config/context/edit.ts b/src/commands/config/context/edit.ts index f16409e9141..44f5d1d0d9a 100644 --- a/src/commands/config/context/edit.ts +++ b/src/commands/config/context/edit.ts @@ -7,14 +7,15 @@ import { ContextFileEmptyError, } from '../../../core/errors/context-error'; import { helpFlag } from '../../../core/flags/global.flags'; +import { cyan } from 'picocolors'; export default class ContextEdit extends Command { static description = 'Edit a context in the store'; static flags = helpFlag(); static args = { - 'context-name': Args.string({description: 'context name', required: true}), - 'new-spec-file-path': Args.string({description: 'file path of the spec file', required: true}), + 'context-name': Args.string({ description: 'context name', required: true }), + 'new-spec-file-path': Args.string({ description: 'file path of the spec file', required: true }), }; async run() { const { args } = await this.parse(ContextEdit); @@ -23,20 +24,16 @@ export default class ContextEdit extends Command { try { await editContext(contextName, newSpecFilePath); - this.log( - `Edited context "${contextName}".\n\nYou can set it as your current context: asyncapi config context use ${contextName}\nYou can use this context when needed by passing ${contextName} as a parameter: asyncapi validate ${contextName}` - ); + this.log(`🎉 Context ${cyan(contextName)} edited successfully!`); + this.log(`\nYou can set it as your current context:\n ${cyan('asyncapi')} ${cyan('config')} ${cyan('context')} ${cyan('use')} ${cyan(contextName)}`); + this.log(`\nYou can use this context by passing ${cyan(contextName)} as a parameter:\n ${cyan('asyncapi')} ${cyan('validate')} ${cyan(contextName)}`); } catch (e) { if ( e instanceof (MissingContextFileError || ContextFileWrongFormatError) ) { - this.log( - 'You have no context file configured. Run "asyncapi config context init" to initialize it.' - ); - return; + this.error(`Unable to edit context. You have no context file configured.\nRun ${cyan('asyncapi config context init')} to initialize it.`); } else if (e instanceof ContextFileEmptyError) { - this.log(`Context file "${CONTEXT_FILE_PATH}" is empty.`); - return; + this.error(`Context file ${cyan(CONTEXT_FILE_PATH)} is empty.`); } throw e; } diff --git a/src/commands/config/context/init.ts b/src/commands/config/context/init.ts index 40d4dc7ab2c..903a3a81901 100644 --- a/src/commands/config/context/init.ts +++ b/src/commands/config/context/init.ts @@ -2,15 +2,16 @@ import { Args } from '@oclif/core'; import Command from '../../../core/base'; import { initContext } from '../../../core/models/Context'; import { helpFlag } from '../../../core/flags/global.flags'; +import { cyan } from 'picocolors'; export default class ContextInit extends Command { static description = 'Initialize context'; static flags = helpFlag(); static contextFilePathMessage = `Specify directory in which context file should be created: - - current directory : asyncapi config context init . (default) - - root of current repository : asyncapi config context init ./ - - user's home directory : asyncapi config context init ~`; + - current directory : ${cyan('asyncapi config context init . ')}(default) + - root of current repository : ${cyan('asyncapi config context init ./ ')} + - user's home directory : ${cyan('asyncapi config context init ~`')}`; static args = { 'context-file-path': Args.string({description: `${ContextInit.contextFilePathMessage}`, required: false}) @@ -21,6 +22,6 @@ export default class ContextInit extends Command { const contextFilePath = args['context-file-path']; const contextWritePath = await initContext(contextFilePath as string); - this.log(`Initialized context ${contextWritePath}`); + this.log(`🎉 Context initialized at ${cyan(contextWritePath)}`); } } diff --git a/src/commands/config/context/list.ts b/src/commands/config/context/list.ts index 2286173c00c..0c4973ebafd 100644 --- a/src/commands/config/context/list.ts +++ b/src/commands/config/context/list.ts @@ -9,6 +9,7 @@ import { ContextFileWrongFormatError, } from '../../../core/errors/context-error'; import { helpFlag } from '../../../core/flags/global.flags'; +import { cyan } from 'picocolors'; export default class ContextList extends Command { static description = 'List all the stored contexts in the store'; @@ -19,7 +20,7 @@ export default class ContextList extends Command { const fileContent = await loadContextFile(); if (await isContextFileEmpty(fileContent)) { - this.log(`Context file "${CONTEXT_FILE_PATH}" is empty.`); + this.log(`Context file ${cyan(CONTEXT_FILE_PATH)} is empty.`); return; } @@ -27,17 +28,14 @@ export default class ContextList extends Command { for (const [contextName, filePath] of Object.entries( fileContent.store )) { - this.log(`${contextName}: ${filePath}`); + this.log(`${cyan(contextName)}: ${filePath}`); } } } catch (e) { if ( e instanceof (MissingContextFileError || ContextFileWrongFormatError) ) { - this.log( - 'You have no context file configured. Run "asyncapi config context init" to initialize it.' - ); - return; + this.error(`Unable to list contexts. You have no context file configured.\nRun ${cyan('asyncapi config context init')} to initialize it.`); } throw e; } diff --git a/src/commands/config/context/remove.ts b/src/commands/config/context/remove.ts index 9a29cd6770d..ffd61587969 100644 --- a/src/commands/config/context/remove.ts +++ b/src/commands/config/context/remove.ts @@ -7,6 +7,7 @@ import { ContextFileEmptyError, } from '../../../core/errors/context-error'; import { helpFlag } from '../../../core/flags/global.flags'; +import { cyan } from 'picocolors'; export default class ContextRemove extends Command { static description = 'Delete a context from the store'; @@ -22,18 +23,14 @@ export default class ContextRemove extends Command { try { await removeContext(contextName); - this.log(`${contextName} successfully deleted`); + this.log(`Context ${cyan(contextName)} removed successfully!`); } catch (e) { if ( e instanceof (MissingContextFileError || ContextFileWrongFormatError) ) { - this.log( - 'You have no context file configured. Run "asyncapi config context init" to initialize it.' - ); - return; + this.error(`Unable to remove context. You have no context file configured.\nRun ${cyan('asyncapi config context init')} to initialize it.`); } else if (e instanceof ContextFileEmptyError) { - this.log(`Context file "${CONTEXT_FILE_PATH}" is empty.`); - return; + this.error(`Context file ${cyan(CONTEXT_FILE_PATH)} is empty.`); } throw e; } diff --git a/src/commands/config/context/use.ts b/src/commands/config/context/use.ts index 6450d524047..06517f7b9f5 100644 --- a/src/commands/config/context/use.ts +++ b/src/commands/config/context/use.ts @@ -7,6 +7,7 @@ import { ContextFileEmptyError, } from '../../../core/errors/context-error'; import { helpFlag } from '../../../core/flags/global.flags'; +import { cyan } from 'picocolors'; export default class ContextUse extends Command { static description = 'Set a context as current'; @@ -22,18 +23,14 @@ export default class ContextUse extends Command { try { await setCurrentContext(contextName); - this.log(`${contextName} is set as current`); + this.log(`Context ${cyan(contextName)} is now set as current.`); } catch (e) { if ( e instanceof (MissingContextFileError || ContextFileWrongFormatError) ) { - this.log( - 'You have no context file configured. Run "asyncapi config context init" to initialize it.' - ); - return; + this.error(`Unable to set the current context. You have no context file configured.\nRun ${cyan('asyncapi config context init')} to initialize it.`); } else if (e instanceof ContextFileEmptyError) { - this.log(`Context file "${CONTEXT_FILE_PATH}" is empty.`); - return; + this.error(`Context file ${cyan(CONTEXT_FILE_PATH)} is empty.`); } throw e; } diff --git a/src/commands/config/versions.ts b/src/commands/config/versions.ts index 915f83ccb3f..0a84ab4dd36 100644 --- a/src/commands/config/versions.ts +++ b/src/commands/config/versions.ts @@ -1,5 +1,6 @@ import Command from '../../core/base'; import { helpFlag } from '../../core/flags/global.flags'; +import { cyan, gray } from 'picocolors'; export default class Versions extends Command { static description = 'Show versions of AsyncAPI tools used'; @@ -30,7 +31,7 @@ export default class Versions extends Command { } // Showing information available with `--version` flag. - this.log(this.config.userAgent); + this.log(gray(`\n${this.config.userAgent}\n`)); // Iteration through the array containing all dependencies '@asyncapi/*' // along with their versions. @@ -49,6 +50,6 @@ export default class Versions extends Command { } } - this.log(`Repository: ${this.config.pjson.homepage}`); + this.log(`Repository: ${cyan(this.config.pjson.homepage)}`); } }