Aside from the expanded feature set, there are some minor differences between Yargs and Black Flag. They should not be relevant given proper use of Black Flag, but are noted below nonetheless.
-
The
yargs::argv
magic property is soft-disabled (always returnsundefined
) because having such an expensive "hot" getter is not optimal in a language where properties can be accessed unpredictably. For instance, deep cloning a Yargs instance results inyargs::parse
(and the handlers of any registered commands!) getting invoked several times, even after an error occurred in an earlier invocation. This can lead to undefined or even dangerous behavior.Who in their right mind is out here cloning Yargs instances, you may ask? Jest does so whenever you use certain asymmetric matchers.
Regardless, you should never have to reach below Black Flag's abstraction over Yargs to call methods like
yargs::parse
,yargs::parseAsync
,yargs::argv
, etc. Instead, just use Black Flag as intended.Therefore, this is effectively a non-issue with proper declarative use of Black Flag.
-
Yargs middleware, while technically supported at the command level, isn't supported CLI-wide since the functionality is covered by configuration hooks.
If you have a Yargs middleware function you want run with a specific command, pass it to
yargs::middleware
via that command'sbuilder
function. If you have a middleware function you want to apply across all commands in your CLI, invoke it inconfigureArguments
. If neither solution is desirable, you can also muck about with the relevant Yargs instances manually inconfigureExecutionPrologue
(example). -
By default, Black Flag enables the
--help
and--version
options same as vanilla Yargs. However, since vanilla Yargs lacks the ability to modify or remove options added byyargs::option
, callingyargs::help
/yargs::version
will throw. If you require the functionality ofyargs::help
/yargs::version
to disable or modify the--help
/--version
option, updateExecutionContext::state.globalHelpOption
/ExecutionContext::state.globalVersionOption
directly inconfigureExecutionContext
(example).
Note
Black Flag enables built-in help and version options, never a help or version command.
Note
Only the root command supports the built-in --version
option. Calling
--version
on a child command will have no effect unless you make it so. This
dodges another Yargs footgun, and setting
ExecutionContext::state.globalVersionOption = undefined
will prevent
Yargs from clobbering any custom version arguments on the root command too.
-
A bug in [email protected] prevents
yargs::showHelp
/--help
from printing anything when using an asyncbuilder
function (or promise-returning function) for a Yargs default command.Black Flag addresses this with its types, in that attempting to pass an async builder will be flagged as problematic by intellisense and trigger an assertion error. Moreover, Black Flag supports an asynchronous function as the value of
module.exports
in CJS code, and top-level await in ESM code, so if you really do need an asyncbuilder
function, hoist the async logic to work around this bug for now. -
A bug? in [email protected] causes
yargs::showHelp
to erroneously print the second element in theyargs::aliases
array of the Yargs default command when said command also has child commands.Black Flag addresses this by using a "helper" program to generate help text more consistently than vanilla Yargs. For instance, the default help text for a Black Flag command includes the full
command
anddescription
strings while the commands under"Commands:"
are listed in alpha-sort order as their full canonical names only; unlike vanilla Yargs, no positional arguments or aliases will be confusingly mixed into help text output unless you make it so. -
As of [email protected], attempting to add two sibling commands with the exact same name causes all sorts of runtime insanity, especially if the commands also have aliases.
Black Flag prevents you from shooting yourself in the foot with this. Specifically: Black Flag will throw if you attempt to add a command with a name or alias that conflicts with its sibling commands' name or alias.
-
As of [email protected], and similar to the above point, attempting to add two options with conflicting names/aliases to the same command leads to undefined and potentially dangerous runtime behavior from Yargs.
Unfortunately, since Yargs allows adding options through a wide variety of means, Black Flag cannot protect you from this footgun. However, Black Flag Extensions (BFE) can.
Specifically: BFE will throw if you attempt to add a command option with a name or alias that conflicts another of that command's options. BFE also takes into account the following yargs-parser settings configuration settings:
camel-case-expansion
,strip-aliased
,strip-dashed
. See BFE's documentation for details. -
Unfortunately, [email protected] doesn't really support calling
yargs::parse
oryargs::parseAsync
multiple times on the same instance if it's using the commands-based API. This might be a regression since, among other things, there are comments within Yargs's source that indicate these functions were intended to be called multiple times.Black Flag addresses this in two ways. First, the
runProgram
helper takes care of state isolation for you, making it safe to callrunProgram
multiple times. Easy peasy. Second,PreExecutionContext::execute
(the wrapper aroundyargs::parseAsync
) will throw if invoked more than once. -
One of Black Flag's features is simple comprehensive error reporting via the
configureErrorHandlingEpilogue
configuration hook. Therefore, Black Flag's overridden version of theyargs::showHelpOnFail
method will ignore the redundant "message" parameter. If you want that functionality, use said hook to output an epilogue after Yargs outputs an error message, or useyargs::epilogue
/yargs::example
.Also, any invocation of Black Flag's
yargs::showHelpOnFail
method applies globally to all commands in your hierarchy; internally, the method is just updatingExecutionContext::state.showHelpOnFail
. -
Since every auto-discovered command translates into its own Yargs instances, the
command
property, if exported by your command file(s), must start with"$0"
or an error will be thrown. This is also enforced by intellisense. -
The
yargs::check
,yargs::global
, andyargs::onFinishCommand
methods, while they may work as expected on commands and their direct child commands, will not function "globally" across your entire command hierarchy since there are several distinct Yargs instances in play when Black Flag executes.If you want a uniform check or so-called "global" argument to apply to every command across your entire hierarchy, the "Black Flag way" would be to just use normal JavaScript instead: export a shared
builder
function (or high-order function) from a utility file and call it in each of your command files (example). If you want something fancier than that, you can leverageconfigureExecutionPrologue
to callyargs::global
oryargs::check
by hand (example).Similarly,
yargs::onFinishCommand
should only be called when theargv
parameter inbuilder
is notundefined
(i.e. only on effector programs). This would prevent the callback from being executed twice. Further, the "Black Flag way" would be to ditchyargs::onFinishCommand
entirely and use plain old JavaScript and/or theconfigureExecutionPrologue
configuration hook instead. -
Since Black Flag is built from the ground up to be asynchronous, calling
yargs::parseSync
will throw immediately. You shouldn't be calling theyargs::parseX
functions directly anyway. -
Black Flag sets several defaults compared to vanilla Yargs. These defaults are detailed here. If you want to tweak these defaults across all commands, use a shared builder (example). If you want something fancier than that, you can leverage
configureExecutionPrologue
(example). -
For UX reasons, Black Flag will "unwrap" errors of type
CliError
, sending onlyCliError::message
to the terminal when an error occurs. Black Flag will not unwrap Yargs's native errors (because they're usually simple strings already) nor custom errors thrown by the end-developer that do not extendCliError
. -
Exporting an "invalid"
command
string will cause Black Flag to throw (while vanilla Yargs will silently fail).command
strings, if given, must be consistent with the Yargs DSL as described here.