Spaces:
Runtime error
Runtime error
| import arrayify from '../node_modules/array-back/index.mjs' | |
| import * as argvTools from './argv-tools.mjs' | |
| import t from '../node_modules/typical/index.mjs' | |
| import Definition from './option-definition.mjs' | |
| /** | |
| * @module option-definitions | |
| */ | |
| /** | |
| * @alias module:option-definitions | |
| */ | |
| class Definitions extends Array { | |
| /** | |
| * validate option definitions | |
| * @param {boolean} [caseInsensitive=false] - whether arguments will be parsed in a case insensitive manner | |
| * @returns {string} | |
| */ | |
| validate (caseInsensitive) { | |
| const someHaveNoName = this.some(def => !def.name) | |
| if (someHaveNoName) { | |
| halt( | |
| 'INVALID_DEFINITIONS', | |
| 'Invalid option definitions: the `name` property is required on each definition' | |
| ) | |
| } | |
| const someDontHaveFunctionType = this.some(def => def.type && typeof def.type !== 'function') | |
| if (someDontHaveFunctionType) { | |
| halt( | |
| 'INVALID_DEFINITIONS', | |
| 'Invalid option definitions: the `type` property must be a setter fuction (default: `Boolean`)' | |
| ) | |
| } | |
| let invalidOption | |
| const numericAlias = this.some(def => { | |
| invalidOption = def | |
| return t.isDefined(def.alias) && t.isNumber(def.alias) | |
| }) | |
| if (numericAlias) { | |
| halt( | |
| 'INVALID_DEFINITIONS', | |
| 'Invalid option definition: to avoid ambiguity an alias cannot be numeric [--' + invalidOption.name + ' alias is -' + invalidOption.alias + ']' | |
| ) | |
| } | |
| const multiCharacterAlias = this.some(def => { | |
| invalidOption = def | |
| return t.isDefined(def.alias) && def.alias.length !== 1 | |
| }) | |
| if (multiCharacterAlias) { | |
| halt( | |
| 'INVALID_DEFINITIONS', | |
| 'Invalid option definition: an alias must be a single character' | |
| ) | |
| } | |
| const hypenAlias = this.some(def => { | |
| invalidOption = def | |
| return def.alias === '-' | |
| }) | |
| if (hypenAlias) { | |
| halt( | |
| 'INVALID_DEFINITIONS', | |
| 'Invalid option definition: an alias cannot be "-"' | |
| ) | |
| } | |
| const duplicateName = hasDuplicates(this.map(def => caseInsensitive ? def.name.toLowerCase() : def.name)) | |
| if (duplicateName) { | |
| halt( | |
| 'INVALID_DEFINITIONS', | |
| 'Two or more option definitions have the same name' | |
| ) | |
| } | |
| const duplicateAlias = hasDuplicates(this.map(def => caseInsensitive && t.isDefined(def.alias) ? def.alias.toLowerCase() : def.alias)) | |
| if (duplicateAlias) { | |
| halt( | |
| 'INVALID_DEFINITIONS', | |
| 'Two or more option definitions have the same alias' | |
| ) | |
| } | |
| const duplicateDefaultOption = this.filter(def => def.defaultOption === true).length > 1; | |
| if (duplicateDefaultOption) { | |
| halt( | |
| 'INVALID_DEFINITIONS', | |
| 'Only one option definition can be the defaultOption' | |
| ) | |
| } | |
| const defaultBoolean = this.some(def => { | |
| invalidOption = def | |
| return def.isBoolean() && def.defaultOption | |
| }) | |
| if (defaultBoolean) { | |
| halt( | |
| 'INVALID_DEFINITIONS', | |
| `A boolean option ["${invalidOption.name}"] can not also be the defaultOption.` | |
| ) | |
| } | |
| } | |
| /** | |
| * Get definition by option arg (e.g. `--one` or `-o`) | |
| * @param {string} [arg] the argument name to get the definition for | |
| * @param {boolean} [caseInsensitive] whether to use case insensitive comparisons when finding the appropriate definition | |
| * @returns {Definition} | |
| */ | |
| get (arg, caseInsensitive) { | |
| if (argvTools.isOption(arg)) { | |
| if (argvTools.re.short.test(arg)) { | |
| const shortOptionName = argvTools.getOptionName(arg) | |
| if (caseInsensitive) { | |
| const lowercaseShortOptionName = shortOptionName.toLowerCase() | |
| return this.find(def => t.isDefined(def.alias) && def.alias.toLowerCase() === lowercaseShortOptionName) | |
| } else { | |
| return this.find(def => def.alias === shortOptionName) | |
| } | |
| } else { | |
| const optionName = argvTools.getOptionName(arg) | |
| if (caseInsensitive) { | |
| const lowercaseOptionName = optionName.toLowerCase() | |
| return this.find(def => def.name.toLowerCase() === lowercaseOptionName) | |
| } else { | |
| return this.find(def => def.name === optionName) | |
| } | |
| } | |
| } else { | |
| return this.find(def => def.name === arg) | |
| } | |
| } | |
| getDefault () { | |
| return this.find(def => def.defaultOption === true) | |
| } | |
| isGrouped () { | |
| return this.some(def => def.group) | |
| } | |
| whereGrouped () { | |
| return this.filter(containsValidGroup) | |
| } | |
| whereNotGrouped () { | |
| return this.filter(def => !containsValidGroup(def)) | |
| } | |
| whereDefaultValueSet () { | |
| return this.filter(def => t.isDefined(def.defaultValue)) | |
| } | |
| static from (definitions, caseInsensitive) { | |
| if (definitions instanceof this) return definitions | |
| const result = super.from(arrayify(definitions), def => Definition.create(def)) | |
| result.validate(caseInsensitive) | |
| return result | |
| } | |
| } | |
| function halt (name, message) { | |
| const err = new Error(message) | |
| err.name = name | |
| throw err | |
| } | |
| function containsValidGroup (def) { | |
| return arrayify(def.group).some(group => group) | |
| } | |
| function hasDuplicates (array) { | |
| const items = {} | |
| for (let i = 0; i < array.length; i++) { | |
| const value = array[i] | |
| if (items[value]) { | |
| return true | |
| } else { | |
| if (t.isDefined(value)) items[value] = true | |
| } | |
| } | |
| } | |
| export default Definitions | |