Spaces:
Sleeping
Sleeping
const arrayify = require('array-back') | |
const argvTools = require('argv-tools') | |
const t = require('typical') | |
/** | |
* @module option-definitions | |
*/ | |
/** | |
* @alias module:option-definitions | |
*/ | |
class Definitions extends Array { | |
/** | |
* validate option definitions | |
* @returns {string} | |
*/ | |
validate () { | |
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 => def.name)) | |
if (duplicateName) { | |
halt( | |
'INVALID_DEFINITIONS', | |
'Two or more option definitions have the same name' | |
) | |
} | |
const duplicateAlias = hasDuplicates(this.map(def => def.alias)) | |
if (duplicateAlias) { | |
halt( | |
'INVALID_DEFINITIONS', | |
'Two or more option definitions have the same alias' | |
) | |
} | |
const duplicateDefaultOption = hasDuplicates(this.map(def => def.defaultOption)) | |
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} | |
* @returns {Definition} | |
*/ | |
get (arg) { | |
if (argvTools.isOption(arg)) { | |
return argvTools.re.short.test(arg) | |
? this.find(def => def.alias === argvTools.getOptionName(arg)) | |
: this.find(def => def.name === argvTools.getOptionName(arg)) | |
} 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) { | |
if (definitions instanceof this) return definitions | |
const Definition = require('./option-definition') | |
const result = super.from(arrayify(definitions), def => Definition.create(def)) | |
result.validate() | |
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 | |
} | |
} | |
} | |
module.exports = Definitions | |