Styles
The styles
module handles text styling. It provides a Style
type that behaves like a string consisting of Select Graphic Rendition (SGR ) sequences, and can be used to customize the display of text in terminals that support them.
Styling attributes
A Style
can be created with the style
function, which accepts a list of styling attributes as parameters. The resulting object can be used in any part of the API that expects this type. It can also be concatenated and embedded in text attributes, such as:
- option synopsis and deprecation notice
- help section titles and texts
- error phrases and help phrases
Styles are cumulative, i.e., they can be combined together depending on the kind of styling attribute. For instance, the style tf.bold
can be combined with fg.green
to render text in bold intensity with green foreground color.
In the next sections we present the various styling attributes available.
Not all of the attributes are supported in every terminal, so you should experiment with them before committing your code.
Type faces
The tf
enumeration declares text type faces that can be combined together. They are listed below.
- Enabling attributes:
bold
- bold or increased intensityfaint
- faint, decreased intensity, or dimitalic
- italicunderlined
- underlinedslowlyBlinking
- slowly blinkingrapidlyBlinking
- rapidly blinkinginverse
- reverse video or inverse (flips foreground and background color)invisible
- invisible, concealed or hiddencrossedOut
- crossed-out or strikethroughdoublyUnderlined
- doubly underlinedproportionalSpacing
- proportional spacing
- Font-changing attributes:
primaryFont
- primary fontalternative[1-9]
- alternative font 1 through 9fraktur
- black-letter font
- Ideogram attributes:
ideogramUnderline
- ideogram underline or right side lineideogramDoubleUnderline
- ideogram double underline, or double line on the right sideideogramOverline
- ideogram overline or left side lineideogramDoubleOverline
- ideogram double overline, or double line on the left sideideogramStressMarking
- ideogram stress marking
- Miscellaneous attributes:
framed
- framedencircled
- encircledoverlined
- overlinedsuperscript
- superscriptsubscript
- subscript
- Resetting attributes:
clear
- resets any other preceding SGR attributenotBoldOrFaint
- normal intensity (neither bold nor faint)notItalicNorFraktur
- regular face (neither italic nor black-letter)notUnderlined
- not underlinednotBlinking
- steady (not blinking)notInverse
- positive (not inverse)notInvisible
- visible (reveal, or not hidden)notCrossedOut
- not crossed out (no strikethrough)notProportionalSpacing
- disable proportional spacingnotFramedOrEncircled
- neither framed nor encirclednotOverlined
- not overlinednoIdeogram
- no ideogram attributesnotSuperscriptOrSubscript
- neither superscript nor subscript
Predefined colors
The fg
and bg
enumerations declare foreground and background colors, respectively. They both have the same set of predefined colors, which are listed below.
default
- the default color- Standard colors:
black
- blackred
- redgreen
- greenyellow
- yellowblue
- bluemagenta
- magentacyan
- cyanwhite
- white
- High-intensity colors:
brightBlack
- bright black (gray)brightRed
- bright redbrightGreen
- bright greenbrightYellow
- bright yellowbrightBlue
- bright bluebrightMagenta
- bright magentabrightCyan
- bright cyanbrightWhite
- bright white
Extended colors
In addition to predefined colors, fg
and bg
have a special enumerator called extended
. It must be followed by additional attributes, which can be obtained through one of the auxiliary functions below:
ext8
- creates an 8-bit indexed colorrgb
- creates a 24-bit RGB color
There is also a ul
enumeration with this enumerator, for underline colors. For example, to set the underline color to orange (#FFA500
):
style(ul.extended, rgb(255, 165, 0));
ANSI string
The AnsiString
is an internal component that handles the construction of strings which are meant to be printed on a terminal. It has methods to split, append and wrap strings, with or without control sequences, and is used by the library in different places:
- by both the parser and validator, to create error and warning messages
- by the formatter, to build help messages
The main logic implemented in this class is divided into splitting and wrapping, as is explained in the next sections.
Text splitting
Text is split into paragraphs, list items and words using a combination of simple regular expressions. The features supported in text splitting are described below.
Placeholders
During splitting, numeric placeholders prefixed with a hashtag #
may be extracted from the text and processed by a formatting callback. This is used by the library in different places:
- by both the parser and validator, to produce error/warning messages based on configured error phrases
- by the formatter, to assemble help items from configured help phrases
This particular feature is only available in phrases, not in other text attributes.
Placeholders are meant to be replaced with a value (or values, in case of arrays). Their format depends on the type of the value, which is denoted internally by a single character. The available ones are:
b
- a boolean values
- a string valuen
- a number valuer
- a regular expressionm
- a symbol value (e.g., option name)t
- another ANSI stringu
- a URL hyperlinka
- an array valueo
- an object valuev
- an unknown value (enclosed in angle brackets, like the parameter column)
When a phrase includes placeholders, they indicate where in the phrase to place the corresponding value. If a phrase has multiple alternatives (depending on the context), different pieces of text may be specified separated by a vertical bar and grouped in parentheses, e.g. '(...|...)'
.
Inline styles
Text can contain inline styles. This applies not only to phrases, but to text attributes as well, which the formatter splits into words before adding to the help message.
When splitting text, the ANSI string will try its best to preserve inline styles. However, if you find some corner case that is not currently covered by our unit tests, please submit a bug report .
Paragraphs and lists
Although Markdown syntax is not fully supported, paragraphs and itemized/numbered lists are formatted in a Markdown-ish fashion. For example, the following text
A paragraph with
single line breaks, redundant spacing and:
- itemized; or
1. numbered lists
Another paragraph
Would be rendered as:
A paragraph with single line breaks, redundant spacing and:
- itemized; or
1. numbered lists
Another paragraph
Text wrapping
Splitted text is always wrapped according to a configured width, i.e., the number of terminal columns by which to limit each line of text.
When a width of zero or undefined
is provided (which may happen if the output is being redirected), the ANSI string will by default avoid wrapping and instead suppress some control sequences that would otherwise be used to render the text in a terminal.
The following features are supported in text wrapping.
Indentation level
A ANSI string optionally has an indentation level, i.e., the starting terminal column of each line of text.
When wrapping text to the desired width, the ANSI string will attempt to respect this setting, but if the desired width does not accommodate the length of the largest word, it will instead wrap lines to the first terminal column.
Text alignment
A ANSI string has an optional flag that indicates whether the text should be aligned to the terminal’s right boundary. This feature is used by the formatter in the help message’s description column.
Messages
There are different kinds of content that an application might want to print in a terminal. We call them “messages” and provide a specific class for each kind of message that the library can produce.
For convenience, all message classes have a toString
method and a message
property, both of which can be used to obtain a traditional string
. However, each class uses its own default terminal width to render the resulting string. The various classes are described below.
All messages produced by the library are instances of one of these classes. So you can check the kind of a captured message through instanceof
.
ANSI message
The AnsiMessage
class wraps a list of ANSI strings. It is the type used for help messages. This class provides a wrap
method to get a traditional string, accepting the following optional parameters:
width
- the desired terminal width (use0
to avoid wrapping)emitStyles
- whether styles should be emittedemitSpaces
- whether spaces should be emitted instead of move sequences
The default values of these parameters depend on a few environment variables:
FORCE_WIDTH
- force the terminal width to a specific value (otherwise, see below)FORCE_COLOR
- force emission of styles regardless of the terminal widthNO_COLOR
- force omission of styles regardless of the terminal widthTERM
- set to'dumb'
to achieve the same behavior asNO_COLOR
FORCE_SPACES
- force emission of spaces when the terminal width is non-zero
When FORCE_WIDTH
is not set, the default width used by this class is the columns
property of the standard output stream (i.e., process.stdout
). Therefore, this kind of message should be printed with console.log
or equivalent.
When redirecting the output of a command (e.g., writing to a file or piping to another command), the associated stream may not have a columns
property, thus defaulting to zero.
Generally, you should not alter the returned string after being wrapped, as this will mess up the disposition of text in the terminal. If you do need to, you can either prepend text ending with a line feed '\n'
, or append text starting with a line feed, as this will preserve the disposition of wrapped text. Alternatively, you can retrieve the underlying ANSI strings from the list and manipulate them before converting the message to string.
Warning message
The WarnMessage
class is a specialization of the ANSI message, used for warnings.
When FORCE_WIDTH
is not set, the default width used by this class is the columns
property of the standard error stream (i.e., process.stderr
). Therefore, this kind of message should be printed with console.error
or equivalent.
It also offers two methods that are used by the library:
add
- appends a ANSI message formatted from one of the error phrasesaddCustom
- appends a ANSI message formatted from a custom phrase
Error message
The ErrorMessage
class is a specialization of the standard Error
class, used for errors. It has the same semantics as the warning message, so it should be printed with console.error
or equivalent.
Similarly to the warning message, it offers two static methods that are used by the library:
create
- creates an error message formatted from one of the error phrasescreateCustom
- creates an error message formatted from a custom phrase
Text message
The TextMessage
class represents a list of plain text lines with no wrapping. It is the type used for completion words. It produces a string delimited by line feeds, and should be printed with console.log
or equivalent.
Message configuration
ANSI-based messages produced by the library can be customized via the global config
object. It is a singleton instance of the MessageConfig
type, which contains the optional properties described below.
Message styles
The styles
property specifies the styles of text elements in ANSI messages. It has the following optional settings:
Property | Used for | Default style |
---|---|---|
boolean | Boolean values | fg.blue |
string | String values | fg.green |
number | Number values | fg.yellow |
regex | Regular expressions | fg.red |
symbol | Symbols (e.g., names) | fg.magenta |
value | Unknown values | fg.brightBlack |
url | URLs | fg.cyan |
text | General text | tf.clear |
To preserve previous styles for a particular element, you can assign it an empty style()
. If you need to disable text styling for your application as a whole, do the same for all elements.
Connective words
The connectives
property specifies words used to format some text elements that cannot be customized with phrases, such as option requirements, element separators and string quoting. It has the following optional settings:
Property | Description | Default |
---|---|---|
and | Connects two logical expressions in conjunction | 'and' |
or | Connects two logical expressions in disjunction | 'or' |
not | Connects a logical expression in negation | 'not' |
no | Connects a logical expression in non-existence | 'no' |
equals | Connects two expressions in equality comparison | '==' |
notEquals | Connects two expressions in non-equality comparison | '!=' |
optionAlt | Connects two option names in alternation | '|' |
optionSep | Connects two option names in succession | ',' |
stringQuote | Encloses a string value | "'" |
arraySep | Connects two array elements in succession | ',' |
arrayOpen | Opening bracket of an array value | '[' |
arrayClose | Closing bracket of an array value | ']' |
objectSep | Connects two object entries in succession | ',' |
objectOpen | Opening bracket of an object value | '{' |
objectClose | Closing bracket of an object value | '}' |
valueSep | Connects an object key with its value | ':' |
valueOpen | Opening bracket of an unknown value | '<' |
valueClose | Closing bracket of an unknown value | '>' |
exprOpen | Opening bracket of an expression | '(' |
exprClose | Closing bracket of an expression | ')' |
Connective words should not contain inline styles or line feeds.
Error phrases
The errorPhrases
property specifies custom phrases to use for error and warning messages. It has the following optional settings, whose keys are enumerators from ErrorItem
:
Error item | Raised when | Default phrase | Placeholders |
---|---|---|---|
unknownOption | An option name is not found, with possible name suggestions | 'Unknown option #0.(| Similar names are: #1.)' | #0 = the unknown option name; #1 = the similar names |
unsatisfiedRequirement | An option’s forward requirement is not satisfied | 'Option #0 requires #1.' | #0 = the specified option name; #1 = the requirements |
missingRequiredOption | An option that is always required was not specified | 'Option #0 is required.' | #0 = the option’s preferred name |
missingParameter | An option is specified without one of its expected parameter(s) | 'Missing parameter(s) to option #0: requires (exactly|at least|between) #1.' | #0 = the specified option name; #1 = the parameter count |
versionFileNotFound | A version file could not be found when handling the version option | 'Could not find a version JSON file.' | |
disallowedInlineParameter | An option is specified with an inline parameter, despite it being disallowed | '(Option|Positional marker) #0 does not accept inline parameters.' | #0 = the specified option name or positional marker |
choiceConstraintViolation | An option parameter fails to satisfy a choice constraint | 'Invalid parameter to #0: #1. Value must be one of: #2.' | #0 = the option’s key or specified name; #1 = the specified value; #2 = the choices |
regexConstraintViolation | An option parameter fails to satisfy a regex constraint | 'Invalid parameter to #0: #1. Value must match the regex #2.' | #0 = the option’s key or specified name; #1 = the specified value; #2 = the regex |
limitConstraintViolation | An option value fails to satisfy a count limit constraint | 'Option #0 has too many values: #1. Should have at most #2.' | #0 = the option’s key or specified name; #1 = the element count; #2 = the limit |
deprecatedOption | A deprecated option is specified on the command-line | 'Option #0 is deprecated and may be removed in future releases.' | #0 = the specified option name |
unsatisfiedCondRequirement | An option’s conditional requirement is not satisfied | 'Option #0 is required if #1.' | #0 = the specified option name; #1 = the requirements |
invalidClusterOption | A variadic option option is specified in the middle of a cluster argument | 'Option letter #0 must be the last in a cluster.' | #0 = the specified cluster letter |
missingInlineParameter | An option is specified with no inline parameter, despite it being required | 'Option #0 requires an inline parameter.' | #0 = the specified option name |
invalidOptionName | An option has an invalid name | 'Option #0 has invalid name #1.' | #0 = the option’s key; #1 = the invalid name |
invalidSelfRequirement | An option references itself in a requirement | 'Option #0 requires itself.' | #0 = the option’s key |
unknownRequiredOption | An option references an unknown option in a requirement | 'Unknown option #0 in requirement.' | #0 = the required option’s key |
invalidRequiredOption | An option references a non-valued option in a requirement | 'Invalid option #0 in requirement.' | #0 = the required option’s key |
invalidRequiredValue | An option uses an invalid value in a requirement | 'Invalid required value for option #0. Option is always required or has a default value.' | #0 = the required option’s key |
duplicateOptionName | There are two identical option names | 'Option #0 has duplicate name #1.' | #0 = the option’s key; #1 = the duplicate name |
duplicatePositionalOption | There are two or more positional options | 'Duplicate positional option #0: previous was #1.' | #0 = the duplicate option’s key; #1 = the previous option’s key |
duplicateChoiceValue | A choices constraint has a duplicate value | 'Option #0 has duplicate choice #1.' | #0 = the option’s key; #1 = the duplicate value |
duplicateClusterLetter | There are two identical cluster letters | 'Option #0 has duplicate cluster letter #1.' | #0 = the option’s key; #1 = the duplicate letter |
invalidClusterLetter | An option has an invalid cluster letter | 'Option #0 has invalid cluster letter #1.' | #0 = the option’s key; #1 = the invalid letter |
tooSimilarOptionNames | An option name is too similar to other names | '#0: Option name #1 has too similar names: #2.' | #0 = the command prefix1; #1 = the option name; #2 = the similar names |
mixedNamingConvention | A name slot contains names with different naming conventions | '#0: Name slot #1 has mixed naming conventions: #2.' | #0 = the command prefix1; #1 = the name slot index; #2 = the naming conventions |
invalidParamCount | A function option has an invalid parameter count | 'Option #0 has invalid parameter count #1.' | #0 = the option’s key; #1 = the parameter count |
variadicWithClusterLetter | A variadic option declares cluster letters | 'Variadic option #0 may only appear as the last option in a cluster.' | #0 = the option’s key |
invalidInlineConstraint | A variadic option declares an inline constraint | 'Option #0 has invalid inline constraint.' | #0 = the option’s key |
missingResolveCallback | A module needs to be loaded, but a resolution function was not provided | 'Missing module resolution function.' |
Error phrases are formatted according to text formatting rules.
Help phrases
The helpPhrases
property specifies custom phrases to use for each kind of help item that may appear in a help message. It has the following optional settings, whose keys are enumerators from HelpItem
:
Help item | Description | Default phrase | Placeholders |
---|---|---|---|
synopsis | The option’s synopsis | '#0' | #0 = the option’s synopsis |
cluster | The option’s cluster letters | 'Can be clustered with #0.' | #0 = the cluster letters |
separator | The parameter delimiter of a non-niladic option | 'Values can be delimited with #0.' | #0 = the parameter separator |
paramCount | The parameter count of a variadic or polyadic option | 'Accepts (multiple|#0|at most #0|at least #0|between #0) parameters.' | #0 = the parameter count |
positional | Whether the option accepts positional arguments | 'Accepts positional arguments(| that may be preceded by #0).' | #0 = the positional marker |
inline | The option’s treatment of inline parameters | '(Disallows|Requires) inline parameters.' | |
append | Whether an array-valued option can be specified multiple times | 'Can be specified multiple times.' | |
choices | The option’s parameter choices | 'Values must be one of #0.' | #0 = the parameter choices |
regex | The regular expression that parameters should match | 'Values must match the regex #0.' | #0 = the regular expression |
unique | Whether duplicate elements will be removed from an array-valued option value | 'Duplicate values will be removed.' | |
limit | The element count limit of an array-valued option | 'Element count is limited to #0.' | #0 = the count limit |
stdin | Whether the option accepts data from standard input | 'Reads data from standard input.' | |
sources | The option’s environment data sources | 'Reads environment data from #0.' | #0 = the data sources |
requires | The option’s forward requirements | 'Requires #0.' | #0 = the requirements |
required | Whether the option is always required | 'Always required.' | |
requiredIf | The option’s conditional requirements | 'Required if #0.' | #0 = the requirements |
default | The option’s default value | 'Defaults to #0.' | #0 = the default value |
useCommand | Whether a help option uses the next argument as the name of a subcommand | 'Uses the next argument as the name of a subcommand.' | |
useFilter | Whether a help option uses the remaining arguments as option filter | 'Uses the remaining arguments as option filter.' | |
deprecated | The option’s deprecation notice | 'Deprecated for #0.' | #0 = the deprecation notice |
link | The option’s external resource hyperlink | 'Refer to #0 for details.' | #0 = the hyperlink |
Help phrases are formatted according to text formatting rules.
Footnotes
-
The command prefix is a series of option keys interspersed with periods, denoting the current subcommand in a hierarchical option definition. It begins as the empty string and is appended with a subcommand’s key whenever one is encountered. This prefix also appears in other validation errors, prefixing the option’s key. Here’s an example:
cmd1.cmd2.flag
. ↩ ↩2