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
This commit is contained in:
Joakim Carlstein 2023-12-20 09:06:13 +01:00 committed by Joakim Carlstein
parent 7bae76f496
commit f9a16d87a1
14 changed files with 112 additions and 11 deletions

View file

@ -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)

View file

@ -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. 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. 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.

View file

@ -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. 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. 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.

View file

@ -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. 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. 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.

View file

@ -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. 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. 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.

View file

@ -46,6 +46,7 @@ Set the directory where your migrations are located, relative to the project roo
### `reporter` ### `reporter`
**type:** `string | EmigrateReporter | Promise<EmigrateReporter> | (() => Promise<EmigrateReporter>)` **type:** `string | EmigrateReporter | Promise<EmigrateReporter> | (() => Promise<EmigrateReporter>)`
**default:** `"default"` - the default reporter **default:** `"default"` - the default reporter
Set the reporter to use for the different commands. Specifying a <Link href="/plugins/reporters/">reporter</Link> is most useful in a CI or production environment where you either ship logs or want to have a machine-readable format. Set the reporter to use for the different commands. Specifying a <Link href="/plugins/reporters/">reporter</Link> 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. 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` ### `storage`
**type:** `string | EmigrateStorage | Promise<EmigrateStorage> | (() => Promise<EmigrateStorage>)` **type:** `string | EmigrateStorage | Promise<EmigrateStorage> | (() => Promise<EmigrateStorage>)`

View file

@ -6,6 +6,14 @@ import { getConfig } from './get-config.js';
type Action = (args: string[]) => Promise<void>; type Action = (args: string[]) => Promise<void>;
const useColors = (values: { color?: boolean; 'no-color'?: boolean }) => {
if (values['no-color']) {
return false;
}
return values.color;
};
const up: Action = async (args) => { const up: Action = async (args) => {
const config = await getConfig('up'); const config = await getConfig('up');
const { values } = parseArgs({ const { values } = parseArgs({
@ -36,6 +44,12 @@ const up: Action = async (args) => {
multiple: true, multiple: true,
default: [], default: [],
}, },
color: {
type: 'boolean',
},
'no-color': {
type: 'boolean',
},
}, },
allowPositionals: false, allowPositionals: false,
}); });
@ -52,6 +66,8 @@ Options:
-p, --plugin The plugin(s) to use (can be specified multiple times) -p, --plugin The plugin(s) to use (can be specified multiple times)
-r, --reporter The reporter to use for reporting the migration progress -r, --reporter The reporter to use for reporting the migration progress
--dry List the pending migrations that would be run without actually running them --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: Examples:
@ -71,7 +87,7 @@ Examples:
try { try {
const { default: upCommand } = await import('./commands/up.js'); 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) { } catch (error) {
if (error instanceof ShowUsageError) { if (error instanceof ShowUsageError) {
console.error(error.message, '\n'); console.error(error.message, '\n');
@ -115,6 +131,12 @@ const newMigration: Action = async (args) => {
multiple: true, multiple: true,
default: [], default: [],
}, },
color: {
type: 'boolean',
},
'no-color': {
type: 'boolean',
},
}, },
allowPositionals: true, allowPositionals: true,
}); });
@ -137,6 +159,8 @@ Options:
(if the extension option is not provided the template file's extension will be used) (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 -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) (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 One of the --template, --extension or the --plugin options must be specified
@ -165,7 +189,7 @@ Examples:
try { try {
const { default: newCommand } = await import('./commands/new.js'); 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) { } catch (error) {
if (error instanceof ShowUsageError) { if (error instanceof ShowUsageError) {
console.error(error.message, '\n'); console.error(error.message, '\n');
@ -199,6 +223,12 @@ const list: Action = async (args) => {
type: 'string', type: 'string',
short: 's', short: 's',
}, },
color: {
type: 'boolean',
},
'no-color': {
type: 'boolean',
},
}, },
allowPositionals: false, allowPositionals: false,
}); });
@ -213,6 +243,8 @@ Options:
-d, --directory The directory where the migration files are located (required) -d, --directory The directory where the migration files are located (required)
-r, --reporter The reporter to use for reporting the migrations -r, --reporter The reporter to use for reporting the migrations
-s, --storage The storage to use to get the migration history (required) -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: Examples:
@ -230,7 +262,7 @@ Examples:
try { try {
const { default: listCommand } = await import('./commands/list.js'); 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) { } catch (error) {
if (error instanceof ShowUsageError) { if (error instanceof ShowUsageError) {
console.error(error.message, '\n'); console.error(error.message, '\n');
@ -268,6 +300,12 @@ const remove: Action = async (args) => {
type: 'string', type: 'string',
short: 's', short: 's',
}, },
color: {
type: 'boolean',
},
'no-color': {
type: 'boolean',
},
}, },
allowPositionals: true, allowPositionals: true,
}); });
@ -289,6 +327,8 @@ Options:
-s, --storage The storage to use to get the migration history (required) -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 -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 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: Examples:
@ -306,7 +346,10 @@ Examples:
try { try {
const { default: removeCommand } = await import('./commands/remove.js'); 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) { } catch (error) {
if (error instanceof ShowUsageError) { if (error instanceof ShowUsageError) {
console.error(error.message, '\n'); console.error(error.message, '\n');

View file

@ -10,7 +10,12 @@ import { version } from '../get-package-info.js';
const lazyDefaultReporter = async () => import('../reporters/default.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) { if (!directory) {
throw MissingOptionError.fromOption('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()); const [storage, storageError] = await exec(async () => storagePlugin.initializeStorage());

View file

@ -19,7 +19,7 @@ import { getDuration } from '../get-duration.js';
const lazyDefaultReporter = async () => import('../reporters/default.js'); const lazyDefaultReporter = async () => import('../reporters/default.js');
export default async function newCommand( export default async function newCommand(
{ directory, template, reporter: reporterConfig, plugins = [], extension }: Config, { directory, template, reporter: reporterConfig, plugins = [], extension, color }: Config,
name: string, name: string,
) { ) {
if (!directory) { 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(); const start = process.hrtime();

View file

@ -22,7 +22,7 @@ type ExtraFlags = {
const lazyDefaultReporter = async () => import('../reporters/default.js'); const lazyDefaultReporter = async () => import('../reporters/default.js');
export default async function removeCommand( 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, name: string,
) { ) {
if (!directory) { if (!directory) {
@ -57,7 +57,7 @@ export default async function removeCommand(
return 1; 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)); const [migrationFile, fileError] = await exec(async () => getMigration(cwd, directory, name, !force));

View file

@ -33,6 +33,7 @@ describe('up', () => {
command: 'up', command: 'up',
cwd: '/emigrate', cwd: '/emigrate',
dry: false, dry: false,
color: undefined,
version, version,
directory: 'migrations', directory: 'migrations',
}, },
@ -111,6 +112,7 @@ describe('up', () => {
cwd: '/emigrate', cwd: '/emigrate',
version, version,
dry: false, dry: false,
color: undefined,
directory: 'migrations', directory: 'migrations',
}, },
]); ]);
@ -152,6 +154,7 @@ describe('up', () => {
cwd: '/emigrate', cwd: '/emigrate',
version, version,
dry: true, dry: true,
color: undefined,
directory: 'migrations', directory: 'migrations',
}, },
]); ]);
@ -193,6 +196,7 @@ describe('up', () => {
cwd: '/emigrate', cwd: '/emigrate',
version, version,
dry: false, dry: false,
color: undefined,
directory: 'migrations', directory: 'migrations',
}, },
]); ]);
@ -219,6 +223,7 @@ describe('up', () => {
cwd: '/emigrate', cwd: '/emigrate',
version, version,
dry: false, dry: false,
color: undefined,
directory: 'migrations', directory: 'migrations',
}, },
]); ]);
@ -265,6 +270,7 @@ describe('up', () => {
cwd: '/emigrate', cwd: '/emigrate',
version, version,
dry: false, dry: false,
color: undefined,
directory: 'migrations', directory: 'migrations',
}, },
]); ]);
@ -312,6 +318,7 @@ describe('up', () => {
cwd: '/emigrate', cwd: '/emigrate',
version, version,
dry: false, dry: false,
color: undefined,
directory: 'migrations', directory: 'migrations',
}, },
]); ]);

View file

@ -25,6 +25,7 @@ export default async function upCommand({
storage: storageConfig, storage: storageConfig,
reporter: reporterConfig, reporter: reporterConfig,
directory, directory,
color,
dry = false, dry = false,
plugins = [], plugins = [],
cwd = process.cwd(), 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()); const [storage, storageError] = await exec(async () => storagePlugin.initializeStorage());

View file

@ -11,6 +11,7 @@ export type Config = {
directory?: string; directory?: string;
template?: string; template?: string;
extension?: string; extension?: string;
color?: boolean;
}; };
export type EmigrateConfig = Config & { export type EmigrateConfig = Config & {

View file

@ -229,6 +229,13 @@ export type ReporterInitParameters = {
* Will only be true when the command is 'up' and the --dry option is specified. * Will only be true when the command is 'up' and the --dry option is specified.
*/ */
dry: boolean; 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<{ export type EmigrateReporter = Partial<{