import * as argvTools from './argv-tools.mjs' import Definitions from './option-definitions.mjs' import findReplace from '../node_modules/find-replace/dist/index.mjs' import t from '../node_modules/typical/index.mjs' /** * @module argv-parser */ /** * @alias module:argv-parser */ class ArgvParser { /** * @param {OptionDefinitions} - Definitions array * @param {object} [options] - Options * @param {string[]} [options.argv] - Overrides `process.argv` * @param {boolean} [options.stopAtFirstUnknown] - * @param {boolean} [options.caseInsensitive] - Arguments will be parsed in a case insensitive manner. Defaults to false. */ constructor (definitions, options) { this.options = Object.assign({}, options) /** * Option Definitions */ this.definitions = Definitions.from(definitions, this.options.caseInsensitive) /** * Argv */ this.argv = argvTools.ArgvArray.from(this.options.argv) if (this.argv.hasCombinedShortOptions()) { findReplace(this.argv, argvTools.re.combinedShort.test.bind(argvTools.re.combinedShort), arg => { arg = arg.slice(1) return arg.split('').map(letter => ({ origArg: `-${arg}`, arg: '-' + letter })) }) } } /** * Yields one `{ event, name, value, arg, def }` argInfo object for each arg in `process.argv` (or `options.argv`). */ * [Symbol.iterator] () { const definitions = this.definitions let def let value let name let event let singularDefaultSet = false let unknownFound = false let origArg for (let arg of this.argv) { if (t.isPlainObject(arg)) { origArg = arg.origArg arg = arg.arg } if (unknownFound && this.options.stopAtFirstUnknown) { yield { event: 'unknown_value', arg, name: '_unknown', value: undefined } continue } /* handle long or short option */ if (argvTools.isOption(arg)) { def = definitions.get(arg, this.options.caseInsensitive) value = undefined if (def) { value = def.isBoolean() ? true : null event = 'set' } else { event = 'unknown_option' } /* handle --option-value notation */ } else if (argvTools.isOptionEqualsNotation(arg)) { const matches = arg.match(argvTools.re.optEquals) def = definitions.get(matches[1], this.options.caseInsensitive) if (def) { if (def.isBoolean()) { yield { event: 'unknown_value', arg, name: '_unknown', value, def } event = 'set' value = true } else { event = 'set' value = matches[2] } } else { event = 'unknown_option' } /* handle value */ } else if (argvTools.isValue(arg)) { if (def) { value = arg event = 'set' } else { /* get the defaultOption */ def = this.definitions.getDefault() if (def && !singularDefaultSet) { value = arg event = 'set' } else { event = 'unknown_value' def = undefined } } } name = def ? def.name : '_unknown' const argInfo = { event, arg, name, value, def } if (origArg) { argInfo.subArg = arg argInfo.arg = origArg } yield argInfo /* unknownFound logic */ if (name === '_unknown') unknownFound = true /* singularDefaultSet logic */ if (def && def.defaultOption && !def.isMultiple() && event === 'set') singularDefaultSet = true /* reset values once consumed and yielded */ if (def && def.isBoolean()) def = undefined /* reset the def if it's a singular which has been set */ if (def && !def.multiple && t.isDefined(value) && value !== null) { def = undefined } value = undefined event = undefined name = undefined origArg = undefined } } } export default ArgvParser