| const { InvalidArgumentError } = require('./error.js'); |
|
|
| class Option { |
| |
| |
| |
| |
| |
| |
|
|
| constructor(flags, description) { |
| this.flags = flags; |
| this.description = description || ''; |
|
|
| this.required = flags.includes('<'); |
| this.optional = flags.includes('['); |
| |
| this.variadic = /\w\.\.\.[>\]]$/.test(flags); |
| this.mandatory = false; |
| const optionFlags = splitOptionFlags(flags); |
| this.short = optionFlags.shortFlag; |
| this.long = optionFlags.longFlag; |
| this.negate = false; |
| if (this.long) { |
| this.negate = this.long.startsWith('--no-'); |
| } |
| this.defaultValue = undefined; |
| this.defaultValueDescription = undefined; |
| this.presetArg = undefined; |
| this.envVar = undefined; |
| this.parseArg = undefined; |
| this.hidden = false; |
| this.argChoices = undefined; |
| this.conflictsWith = []; |
| this.implied = undefined; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
|
|
| default(value, description) { |
| this.defaultValue = value; |
| this.defaultValueDescription = description; |
| return this; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| preset(arg) { |
| this.presetArg = arg; |
| return this; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| conflicts(names) { |
| this.conflictsWith = this.conflictsWith.concat(names); |
| return this; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| implies(impliedOptionValues) { |
| let newImplied = impliedOptionValues; |
| if (typeof impliedOptionValues === 'string') { |
| |
| newImplied = { [impliedOptionValues]: true }; |
| } |
| this.implied = Object.assign(this.implied || {}, newImplied); |
| return this; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| env(name) { |
| this.envVar = name; |
| return this; |
| } |
|
|
| |
| |
| |
| |
| |
| |
|
|
| argParser(fn) { |
| this.parseArg = fn; |
| return this; |
| } |
|
|
| |
| |
| |
| |
| |
| |
|
|
| makeOptionMandatory(mandatory = true) { |
| this.mandatory = !!mandatory; |
| return this; |
| } |
|
|
| |
| |
| |
| |
| |
| |
|
|
| hideHelp(hide = true) { |
| this.hidden = !!hide; |
| return this; |
| } |
|
|
| |
| |
| |
|
|
| _concatValue(value, previous) { |
| if (previous === this.defaultValue || !Array.isArray(previous)) { |
| return [value]; |
| } |
|
|
| return previous.concat(value); |
| } |
|
|
| |
| |
| |
| |
| |
| |
|
|
| choices(values) { |
| this.argChoices = values.slice(); |
| this.parseArg = (arg, previous) => { |
| if (!this.argChoices.includes(arg)) { |
| throw new InvalidArgumentError( |
| `Allowed choices are ${this.argChoices.join(', ')}.`, |
| ); |
| } |
| if (this.variadic) { |
| return this._concatValue(arg, previous); |
| } |
| return arg; |
| }; |
| return this; |
| } |
|
|
| |
| |
| |
| |
| |
|
|
| name() { |
| if (this.long) { |
| return this.long.replace(/^--/, ''); |
| } |
| return this.short.replace(/^-/, ''); |
| } |
|
|
| |
| |
| |
| |
| |
| |
|
|
| attributeName() { |
| if (this.negate) { |
| return camelcase(this.name().replace(/^no-/, '')); |
| } |
| return camelcase(this.name()); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
|
|
| is(arg) { |
| return this.short === arg || this.long === arg; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| isBoolean() { |
| return !this.required && !this.optional && !this.negate; |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| class DualOptions { |
| |
| |
| |
| constructor(options) { |
| this.positiveOptions = new Map(); |
| this.negativeOptions = new Map(); |
| this.dualOptions = new Set(); |
| options.forEach((option) => { |
| if (option.negate) { |
| this.negativeOptions.set(option.attributeName(), option); |
| } else { |
| this.positiveOptions.set(option.attributeName(), option); |
| } |
| }); |
| this.negativeOptions.forEach((value, key) => { |
| if (this.positiveOptions.has(key)) { |
| this.dualOptions.add(key); |
| } |
| }); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| valueFromOption(value, option) { |
| const optionKey = option.attributeName(); |
| if (!this.dualOptions.has(optionKey)) return true; |
|
|
| |
| const preset = this.negativeOptions.get(optionKey).presetArg; |
| const negativeValue = preset !== undefined ? preset : false; |
| return option.negate === (negativeValue === value); |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
|
|
| function camelcase(str) { |
| return str.split('-').reduce((str, word) => { |
| return str + word[0].toUpperCase() + word.slice(1); |
| }); |
| } |
|
|
| |
| |
| |
| |
| |
|
|
| function splitOptionFlags(flags) { |
| let shortFlag; |
| let longFlag; |
| |
| const shortFlagExp = /^-[^-]$/; |
| |
| const longFlagExp = /^--[^-]/; |
|
|
| const flagParts = flags.split(/[ |,]+/).concat('guard'); |
| |
| if (shortFlagExp.test(flagParts[0])) shortFlag = flagParts.shift(); |
| if (longFlagExp.test(flagParts[0])) longFlag = flagParts.shift(); |
| |
| if (!shortFlag && shortFlagExp.test(flagParts[0])) |
| shortFlag = flagParts.shift(); |
| |
| |
| if (!shortFlag && longFlagExp.test(flagParts[0])) { |
| shortFlag = longFlag; |
| longFlag = flagParts.shift(); |
| } |
|
|
| |
| if (flagParts[0].startsWith('-')) { |
| const unsupportedFlag = flagParts[0]; |
| const baseError = `option creation failed due to '${unsupportedFlag}' in option flags '${flags}'`; |
| if (/^-[^-][^-]/.test(unsupportedFlag)) |
| throw new Error( |
| `${baseError} |
| - a short flag is a single dash and a single character |
| - either use a single dash and a single character (for a short flag) |
| - or use a double dash for a long option (and can have two, like '--ws, --workspace')`, |
| ); |
| if (shortFlagExp.test(unsupportedFlag)) |
| throw new Error(`${baseError} |
| - too many short flags`); |
| if (longFlagExp.test(unsupportedFlag)) |
| throw new Error(`${baseError} |
| - too many long flags`); |
|
|
| throw new Error(`${baseError} |
| - unrecognised flag format`); |
| } |
| if (shortFlag === undefined && longFlag === undefined) |
| throw new Error( |
| `option creation failed due to no flags found in '${flags}'.`, |
| ); |
|
|
| return { shortFlag, longFlag }; |
| } |
|
|
| exports.Option = Option; |
| exports.DualOptions = DualOptions; |
|
|