supercat666's picture
add igv
78c921d
raw
history blame
No virus
5.37 kB
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