From f9a16d87a1b874623d0bd9cc9efed241f4499406 Mon Sep 17 00:00:00 2001 From: Joakim Carlstein Date: Wed, 20 Dec 2023 09:06:13 +0100 Subject: [PATCH] feat: add `color` option to CLI and configuration file The option is used to force enable/disable color output and is passed to the reporter which should respect it --- .changeset/funny-ladybugs-cheat.md | 6 +++ docs/src/content/docs/commands/list.mdx | 4 ++ docs/src/content/docs/commands/new.mdx | 4 ++ docs/src/content/docs/commands/remove.mdx | 4 ++ docs/src/content/docs/commands/up.mdx | 4 ++ .../content/docs/reference/configuration.mdx | 15 ++++++ packages/cli/src/cli.ts | 51 +++++++++++++++++-- packages/cli/src/commands/list.ts | 9 +++- packages/cli/src/commands/new.ts | 4 +- packages/cli/src/commands/remove.ts | 4 +- packages/cli/src/commands/up.test.ts | 7 +++ packages/cli/src/commands/up.ts | 3 +- packages/cli/src/types.ts | 1 + packages/types/src/index.ts | 7 +++ 14 files changed, 112 insertions(+), 11 deletions(-) create mode 100644 .changeset/funny-ladybugs-cheat.md diff --git a/.changeset/funny-ladybugs-cheat.md b/.changeset/funny-ladybugs-cheat.md new file mode 100644 index 0000000..4d48eb7 --- /dev/null +++ b/.changeset/funny-ladybugs-cheat.md @@ -0,0 +1,6 @@ +--- +'@emigrate/types': minor +'@emigrate/cli': minor +--- + +Add `color` option to the CLI and configuration file, which is used to force enable/disable color output from the reporter (the option is passed to the chosen reporter which should respect it) diff --git a/docs/src/content/docs/commands/list.mdx b/docs/src/content/docs/commands/list.mdx index 7c79628..6120302 100644 --- a/docs/src/content/docs/commands/list.mdx +++ b/docs/src/content/docs/commands/list.mdx @@ -91,3 +91,7 @@ The name can be either a path to a module or a package name. For package names E And then try to load the module/package with the given name. For example, if you want to use the `emigrate-reporter-somereporter` package, you can specify either `emigrate-reporter-somereporter` or just `somereporter` as the name. + +### `--color`, `--no-color` + +Force enable/disable colored output, option is passed to the reporter which should respect it. diff --git a/docs/src/content/docs/commands/new.mdx b/docs/src/content/docs/commands/new.mdx index a411b29..0c0721d 100644 --- a/docs/src/content/docs/commands/new.mdx +++ b/docs/src/content/docs/commands/new.mdx @@ -108,3 +108,7 @@ The name can be either a path to a module or a package name. For package names E And then try to load the module/package with the given name. For example, if you want to use the `emigrate-reporter-somereporter` package, you can specify either `emigrate-reporter-somereporter` or just `somereporter` as the name. + +### `--color`, `--no-color` + +Force enable/disable colored output, option is passed to the reporter which should respect it. diff --git a/docs/src/content/docs/commands/remove.mdx b/docs/src/content/docs/commands/remove.mdx index d1e8ebe..af17a52 100644 --- a/docs/src/content/docs/commands/remove.mdx +++ b/docs/src/content/docs/commands/remove.mdx @@ -98,3 +98,7 @@ The name can be either a path to a module or a package name. For package names E And then try to load the module/package with the given name. For example, if you want to use the `emigrate-reporter-somereporter` package, you can specify either `emigrate-reporter-somereporter` or just `somereporter` as the name. + +### `--color`, `--no-color` + +Force enable/disable colored output, option is passed to the reporter which should respect it. diff --git a/docs/src/content/docs/commands/up.mdx b/docs/src/content/docs/commands/up.mdx index 3868217..d431807 100644 --- a/docs/src/content/docs/commands/up.mdx +++ b/docs/src/content/docs/commands/up.mdx @@ -112,3 +112,7 @@ The name can be either a path to a module or a package name. For package names E And then try to load the module/package with the given name. For example, if you want to use the `emigrate-reporter-somereporter` package, you can specify either `emigrate-reporter-somereporter` or just `somereporter` as the name. + +### `--color`, `--no-color` + +Force enable/disable colored output, option is passed to the reporter which should respect it. diff --git a/docs/src/content/docs/reference/configuration.mdx b/docs/src/content/docs/reference/configuration.mdx index 5294cc3..46d0a67 100644 --- a/docs/src/content/docs/reference/configuration.mdx +++ b/docs/src/content/docs/reference/configuration.mdx @@ -46,6 +46,7 @@ Set the directory where your migrations are located, relative to the project roo ### `reporter` **type:** `string | EmigrateReporter | Promise | (() => Promise)` + **default:** `"default"` - the default reporter Set the reporter to use for the different commands. Specifying a reporter is most useful in a CI or production environment where you either ship logs or want to have a machine-readable format. @@ -74,6 +75,20 @@ Commands that are not specified will use the default reporter. The default reporter automatically detects if the current environment is an interactive terminal or not, and will only render animations and similar if it is. ::: +### `color` + +**type:** `boolean | undefined` + +**default:** `undefined` + +Set whether to force colors in the output or not. This option is passed to the reporter which should respect it. + +```js title="emigrate.config.js" {2} +export default { + color: false, +}; +``` + ### `storage` **type:** `string | EmigrateStorage | Promise | (() => Promise)` diff --git a/packages/cli/src/cli.ts b/packages/cli/src/cli.ts index de7132b..4ad8ace 100644 --- a/packages/cli/src/cli.ts +++ b/packages/cli/src/cli.ts @@ -6,6 +6,14 @@ import { getConfig } from './get-config.js'; type Action = (args: string[]) => Promise; +const useColors = (values: { color?: boolean; 'no-color'?: boolean }) => { + if (values['no-color']) { + return false; + } + + return values.color; +}; + const up: Action = async (args) => { const config = await getConfig('up'); const { values } = parseArgs({ @@ -36,6 +44,12 @@ const up: Action = async (args) => { multiple: true, default: [], }, + color: { + type: 'boolean', + }, + 'no-color': { + type: 'boolean', + }, }, allowPositionals: false, }); @@ -52,6 +66,8 @@ Options: -p, --plugin The plugin(s) to use (can be specified multiple times) -r, --reporter The reporter to use for reporting the migration progress --dry List the pending migrations that would be run without actually running them + --color Force color output (this option is passed to the reporter) + --no-color Disable color output (this option is passed to the reporter) Examples: @@ -71,7 +87,7 @@ Examples: try { const { default: upCommand } = await import('./commands/up.js'); - process.exitCode = await upCommand({ storage, reporter, directory, plugins, dry }); + process.exitCode = await upCommand({ storage, reporter, directory, plugins, dry, color: useColors(values) }); } catch (error) { if (error instanceof ShowUsageError) { console.error(error.message, '\n'); @@ -115,6 +131,12 @@ const newMigration: Action = async (args) => { multiple: true, default: [], }, + color: { + type: 'boolean', + }, + 'no-color': { + type: 'boolean', + }, }, allowPositionals: true, }); @@ -137,6 +159,8 @@ Options: (if the extension option is not provided the template file's extension will be used) -e, --extension The extension to use for the new migration file (if no template or plugin is provided an empty migration file will be created with the given extension) + --color Force color output (this option is passed to the reporter) + --no-color Disable color output (this option is passed to the reporter) One of the --template, --extension or the --plugin options must be specified @@ -165,7 +189,7 @@ Examples: try { const { default: newCommand } = await import('./commands/new.js'); - await newCommand({ directory, template, plugins, extension, reporter }, name); + await newCommand({ directory, template, plugins, extension, reporter, color: useColors(values) }, name); } catch (error) { if (error instanceof ShowUsageError) { console.error(error.message, '\n'); @@ -199,6 +223,12 @@ const list: Action = async (args) => { type: 'string', short: 's', }, + color: { + type: 'boolean', + }, + 'no-color': { + type: 'boolean', + }, }, allowPositionals: false, }); @@ -213,6 +243,8 @@ Options: -d, --directory The directory where the migration files are located (required) -r, --reporter The reporter to use for reporting the migrations -s, --storage The storage to use to get the migration history (required) + --color Force color output (this option is passed to the reporter) + --no-color Disable color output (this option is passed to the reporter) Examples: @@ -230,7 +262,7 @@ Examples: try { const { default: listCommand } = await import('./commands/list.js'); - process.exitCode = await listCommand({ directory, storage, reporter }); + process.exitCode = await listCommand({ directory, storage, reporter, color: useColors(values) }); } catch (error) { if (error instanceof ShowUsageError) { console.error(error.message, '\n'); @@ -268,6 +300,12 @@ const remove: Action = async (args) => { type: 'string', short: 's', }, + color: { + type: 'boolean', + }, + 'no-color': { + type: 'boolean', + }, }, allowPositionals: true, }); @@ -289,6 +327,8 @@ Options: -s, --storage The storage to use to get the migration history (required) -f, --force Force removal of the migration history entry even if the migration file does not exist or it's in a non-failed state + --color Force color output (this option is passed to the reporter) + --no-color Disable color output (this option is passed to the reporter) Examples: @@ -306,7 +346,10 @@ Examples: try { const { default: removeCommand } = await import('./commands/remove.js'); - process.exitCode = await removeCommand({ directory, storage, reporter, force }, positionals[0] ?? ''); + process.exitCode = await removeCommand( + { directory, storage, reporter, force, color: useColors(values) }, + positionals[0] ?? '', + ); } catch (error) { if (error instanceof ShowUsageError) { console.error(error.message, '\n'); diff --git a/packages/cli/src/commands/list.ts b/packages/cli/src/commands/list.ts index d0ed7b5..726351f 100644 --- a/packages/cli/src/commands/list.ts +++ b/packages/cli/src/commands/list.ts @@ -10,7 +10,12 @@ import { version } from '../get-package-info.js'; const lazyDefaultReporter = async () => import('../reporters/default.js'); -export default async function listCommand({ directory, reporter: reporterConfig, storage: storageConfig }: Config) { +export default async function listCommand({ + directory, + reporter: reporterConfig, + storage: storageConfig, + color, +}: Config) { if (!directory) { throw MissingOptionError.fromOption('directory'); } @@ -31,7 +36,7 @@ export default async function listCommand({ directory, reporter: reporterConfig, ); } - await reporter.onInit?.({ command: 'list', version, cwd, dry: false, directory }); + await reporter.onInit?.({ command: 'list', version, cwd, dry: false, directory, color }); const [storage, storageError] = await exec(async () => storagePlugin.initializeStorage()); diff --git a/packages/cli/src/commands/new.ts b/packages/cli/src/commands/new.ts index 8c551c4..bd4f682 100644 --- a/packages/cli/src/commands/new.ts +++ b/packages/cli/src/commands/new.ts @@ -19,7 +19,7 @@ import { getDuration } from '../get-duration.js'; const lazyDefaultReporter = async () => import('../reporters/default.js'); export default async function newCommand( - { directory, template, reporter: reporterConfig, plugins = [], extension }: Config, + { directory, template, reporter: reporterConfig, plugins = [], extension, color }: Config, name: string, ) { if (!directory) { @@ -45,7 +45,7 @@ export default async function newCommand( ); } - await reporter.onInit?.({ command: 'new', version, cwd, dry: false, directory }); + await reporter.onInit?.({ command: 'new', version, cwd, dry: false, directory, color }); const start = process.hrtime(); diff --git a/packages/cli/src/commands/remove.ts b/packages/cli/src/commands/remove.ts index 852a34e..b74ecaf 100644 --- a/packages/cli/src/commands/remove.ts +++ b/packages/cli/src/commands/remove.ts @@ -22,7 +22,7 @@ type ExtraFlags = { const lazyDefaultReporter = async () => import('../reporters/default.js'); export default async function removeCommand( - { directory, reporter: reporterConfig, storage: storageConfig, force }: Config & ExtraFlags, + { directory, reporter: reporterConfig, storage: storageConfig, color, force = false }: Config & ExtraFlags, name: string, ) { if (!directory) { @@ -57,7 +57,7 @@ export default async function removeCommand( return 1; } - await reporter.onInit?.({ command: 'remove', version, cwd, dry: false, directory }); + await reporter.onInit?.({ command: 'remove', version, cwd, dry: false, directory, color }); const [migrationFile, fileError] = await exec(async () => getMigration(cwd, directory, name, !force)); diff --git a/packages/cli/src/commands/up.test.ts b/packages/cli/src/commands/up.test.ts index c0b63c4..fe88b74 100644 --- a/packages/cli/src/commands/up.test.ts +++ b/packages/cli/src/commands/up.test.ts @@ -33,6 +33,7 @@ describe('up', () => { command: 'up', cwd: '/emigrate', dry: false, + color: undefined, version, directory: 'migrations', }, @@ -111,6 +112,7 @@ describe('up', () => { cwd: '/emigrate', version, dry: false, + color: undefined, directory: 'migrations', }, ]); @@ -152,6 +154,7 @@ describe('up', () => { cwd: '/emigrate', version, dry: true, + color: undefined, directory: 'migrations', }, ]); @@ -193,6 +196,7 @@ describe('up', () => { cwd: '/emigrate', version, dry: false, + color: undefined, directory: 'migrations', }, ]); @@ -219,6 +223,7 @@ describe('up', () => { cwd: '/emigrate', version, dry: false, + color: undefined, directory: 'migrations', }, ]); @@ -265,6 +270,7 @@ describe('up', () => { cwd: '/emigrate', version, dry: false, + color: undefined, directory: 'migrations', }, ]); @@ -312,6 +318,7 @@ describe('up', () => { cwd: '/emigrate', version, dry: false, + color: undefined, directory: 'migrations', }, ]); diff --git a/packages/cli/src/commands/up.ts b/packages/cli/src/commands/up.ts index 07ec7c9..2423286 100644 --- a/packages/cli/src/commands/up.ts +++ b/packages/cli/src/commands/up.ts @@ -25,6 +25,7 @@ export default async function upCommand({ storage: storageConfig, reporter: reporterConfig, directory, + color, dry = false, plugins = [], cwd = process.cwd(), @@ -49,7 +50,7 @@ export default async function upCommand({ ); } - await reporter.onInit?.({ command: 'up', version, cwd, dry, directory }); + await reporter.onInit?.({ command: 'up', version, cwd, dry, directory, color }); const [storage, storageError] = await exec(async () => storagePlugin.initializeStorage()); diff --git a/packages/cli/src/types.ts b/packages/cli/src/types.ts index 2880bd8..bc4e000 100644 --- a/packages/cli/src/types.ts +++ b/packages/cli/src/types.ts @@ -11,6 +11,7 @@ export type Config = { directory?: string; template?: string; extension?: string; + color?: boolean; }; export type EmigrateConfig = Config & { diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index a062519..dea668d 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -229,6 +229,13 @@ export type ReporterInitParameters = { * Will only be true when the command is 'up' and the --dry option is specified. */ dry: boolean; + /** + * Forcibly enable or disable colors in the output. + * + * If set to true, the reporter should use colors in the output. + * If set to false, the reporter should not use colors in the output. + */ + color?: boolean; }; export type EmigrateReporter = Partial<{