123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198 |
- 'use strict';
- const {
- ArrayPrototypeFind,
- ObjectEntries,
- ObjectPrototypeHasOwnProperty: ObjectHasOwn,
- StringPrototypeCharAt,
- StringPrototypeIncludes,
- StringPrototypeStartsWith,
- } = require('./internal/primordials');
- const {
- validateObject,
- } = require('./internal/validators');
- // These are internal utilities to make the parsing logic easier to read, and
- // add lots of detail for the curious. They are in a separate file to allow
- // unit testing, although that is not essential (this could be rolled into
- // main file and just tested implicitly via API).
- //
- // These routines are for internal use, not for export to client.
- /**
- * Return the named property, but only if it is an own property.
- */
- function objectGetOwn(obj, prop) {
- if (ObjectHasOwn(obj, prop))
- return obj[prop];
- }
- /**
- * Return the named options property, but only if it is an own property.
- */
- function optionsGetOwn(options, longOption, prop) {
- if (ObjectHasOwn(options, longOption))
- return objectGetOwn(options[longOption], prop);
- }
- /**
- * Determines if the argument may be used as an option value.
- * @example
- * isOptionValue('V') // returns true
- * isOptionValue('-v') // returns true (greedy)
- * isOptionValue('--foo') // returns true (greedy)
- * isOptionValue(undefined) // returns false
- */
- function isOptionValue(value) {
- if (value == null) return false;
- // Open Group Utility Conventions are that an option-argument
- // is the argument after the option, and may start with a dash.
- return true; // greedy!
- }
- /**
- * Detect whether there is possible confusion and user may have omitted
- * the option argument, like `--port --verbose` when `port` of type:string.
- * In strict mode we throw errors if value is option-like.
- */
- function isOptionLikeValue(value) {
- if (value == null) return false;
- return value.length > 1 && StringPrototypeCharAt(value, 0) === '-';
- }
- /**
- * Determines if `arg` is just a short option.
- * @example '-f'
- */
- function isLoneShortOption(arg) {
- return arg.length === 2 &&
- StringPrototypeCharAt(arg, 0) === '-' &&
- StringPrototypeCharAt(arg, 1) !== '-';
- }
- /**
- * Determines if `arg` is a lone long option.
- * @example
- * isLoneLongOption('a') // returns false
- * isLoneLongOption('-a') // returns false
- * isLoneLongOption('--foo') // returns true
- * isLoneLongOption('--foo=bar') // returns false
- */
- function isLoneLongOption(arg) {
- return arg.length > 2 &&
- StringPrototypeStartsWith(arg, '--') &&
- !StringPrototypeIncludes(arg, '=', 3);
- }
- /**
- * Determines if `arg` is a long option and value in the same argument.
- * @example
- * isLongOptionAndValue('--foo') // returns false
- * isLongOptionAndValue('--foo=bar') // returns true
- */
- function isLongOptionAndValue(arg) {
- return arg.length > 2 &&
- StringPrototypeStartsWith(arg, '--') &&
- StringPrototypeIncludes(arg, '=', 3);
- }
- /**
- * Determines if `arg` is a short option group.
- *
- * See Guideline 5 of the [Open Group Utility Conventions](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap12.html).
- * One or more options without option-arguments, followed by at most one
- * option that takes an option-argument, should be accepted when grouped
- * behind one '-' delimiter.
- * @example
- * isShortOptionGroup('-a', {}) // returns false
- * isShortOptionGroup('-ab', {}) // returns true
- * // -fb is an option and a value, not a short option group
- * isShortOptionGroup('-fb', {
- * options: { f: { type: 'string' } }
- * }) // returns false
- * isShortOptionGroup('-bf', {
- * options: { f: { type: 'string' } }
- * }) // returns true
- * // -bfb is an edge case, return true and caller sorts it out
- * isShortOptionGroup('-bfb', {
- * options: { f: { type: 'string' } }
- * }) // returns true
- */
- function isShortOptionGroup(arg, options) {
- if (arg.length <= 2) return false;
- if (StringPrototypeCharAt(arg, 0) !== '-') return false;
- if (StringPrototypeCharAt(arg, 1) === '-') return false;
- const firstShort = StringPrototypeCharAt(arg, 1);
- const longOption = findLongOptionForShort(firstShort, options);
- return optionsGetOwn(options, longOption, 'type') !== 'string';
- }
- /**
- * Determine if arg is a short string option followed by its value.
- * @example
- * isShortOptionAndValue('-a', {}); // returns false
- * isShortOptionAndValue('-ab', {}); // returns false
- * isShortOptionAndValue('-fFILE', {
- * options: { foo: { short: 'f', type: 'string' }}
- * }) // returns true
- */
- function isShortOptionAndValue(arg, options) {
- validateObject(options, 'options');
- if (arg.length <= 2) return false;
- if (StringPrototypeCharAt(arg, 0) !== '-') return false;
- if (StringPrototypeCharAt(arg, 1) === '-') return false;
- const shortOption = StringPrototypeCharAt(arg, 1);
- const longOption = findLongOptionForShort(shortOption, options);
- return optionsGetOwn(options, longOption, 'type') === 'string';
- }
- /**
- * Find the long option associated with a short option. Looks for a configured
- * `short` and returns the short option itself if a long option is not found.
- * @example
- * findLongOptionForShort('a', {}) // returns 'a'
- * findLongOptionForShort('b', {
- * options: { bar: { short: 'b' } }
- * }) // returns 'bar'
- */
- function findLongOptionForShort(shortOption, options) {
- validateObject(options, 'options');
- const longOptionEntry = ArrayPrototypeFind(
- ObjectEntries(options),
- ({ 1: optionConfig }) => objectGetOwn(optionConfig, 'short') === shortOption
- );
- return longOptionEntry?.[0] ?? shortOption;
- }
- /**
- * Check if the given option includes a default value
- * and that option has not been set by the input args.
- *
- * @param {string} longOption - long option name e.g. 'foo'
- * @param {object} optionConfig - the option configuration properties
- * @param {object} values - option values returned in `values` by parseArgs
- */
- function useDefaultValueOption(longOption, optionConfig, values) {
- return objectGetOwn(optionConfig, 'default') !== undefined &&
- values[longOption] === undefined;
- }
- module.exports = {
- findLongOptionForShort,
- isLoneLongOption,
- isLoneShortOption,
- isLongOptionAndValue,
- isOptionValue,
- isOptionLikeValue,
- isShortOptionAndValue,
- isShortOptionGroup,
- useDefaultValueOption,
- objectGetOwn,
- optionsGetOwn,
- };
|