UNPKG

@thisismanta/pessimist

Version:

This is a Node.js library that helps derive `process.argv` array into a flexible, value-strict, TypeScript-friendly object.

191 lines (190 loc) 8.38 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.parseArguments = parseArguments; const kebabCase_1 = __importDefault(require("lodash/kebabCase")); const parseBoolean_1 = require("./parseBoolean"); function parseArguments(inputs, defaultHash, options) { var _a, _b, _c, _d; const logicHash = {}; for (const name in defaultHash) { const formalName = name; if (name.length === 1 && name !== '-') { logicHash['-' + name] = { formalName, negated: false }; } else { const kebabName = (0, kebabCase_1.default)(name); logicHash['--' + kebabName] = { formalName, negated: false }; if (kebabName.startsWith('no-')) { logicHash['--' + kebabName.substring(3)] = { formalName, negated: true }; } else { logicHash['--no-' + kebabName] = { formalName, negated: true }; } } } if (typeof options?.aliases === 'object' && options.aliases !== null) { for (const aliasName in options.aliases) { const targetName = options.aliases[aliasName]; if (typeof targetName !== 'string') { throw new Error(`Expected the right-hand side of the alias to be a string: ${aliasName}${targetName}`); } const negationOperatorCount = targetName.match(/^\!+/)?.[0].length ?? 0; const formalName = targetName.substring(negationOperatorCount); const negated = negationOperatorCount % 2 === 1; if (!isKnownName(formalName)) { throw new Error(`Expected the right-hand side of the alias to be one of the known names: ${aliasName}${targetName}`); } if (aliasName.length === 1 && aliasName !== '-') { logicHash[_a = '-' + aliasName] ?? (logicHash[_a] = { formalName, negated }); } else { const kebabName = (0, kebabCase_1.default)(aliasName); logicHash[_b = '--' + kebabName] ?? (logicHash[_b] = { formalName, negated }); if (kebabName.startsWith('no-')) { logicHash[_c = '--' + kebabName.substring(3)] ?? (logicHash[_c] = { formalName, negated: !negated }); } else { logicHash[_d = '--no-' + kebabName] ?? (logicHash[_d] = { formalName, negated: !negated }); } } } } const [positionalArguments, namedArguments] = inputs .filter(input => /^-+$/.test(input) === false) .reduce(([positionalArguments, namedArguments], raw) => { if (raw.startsWith('--')) { const delimiterIndex = raw.indexOf('='); const actualName = delimiterIndex === -1 ? raw : raw.substring(0, delimiterIndex); const value = delimiterIndex === -1 ? undefined : raw.substring(delimiterIndex + 1); const kebabName = '--' + (0, kebabCase_1.default)(actualName); if (kebabName in logicHash === false) { throw new Error(`Unexpected an unknown argument: ${raw}`); } const { formalName, negated } = logicHash[kebabName]; namedArguments.push({ raw, formalName, negated, value, }); } else if (raw.startsWith('-')) { const delimiterIndex = raw.indexOf('='); if (delimiterIndex === -1) { for (const name of raw.substring(1).split('')) { const kebabName = '-' + name; if (kebabName in logicHash === false) { throw new Error(`Unexpected an unknown argument: ${raw}`); } const { formalName, negated } = logicHash[kebabName]; if (typeof defaultHash[formalName] !== 'boolean') { throw new Error(`Unexpected the short-hand argument ${raw} which is a non-Boolean`); } namedArguments.push({ raw, formalName, negated, value: undefined, }); } } else if (delimiterIndex === 2) { const actualName = raw.substring(0, delimiterIndex); if (actualName in logicHash === false) { throw new Error(`Unexpected an unknown argument: ${raw}`); } const { formalName, negated } = logicHash[actualName]; const value = raw.substring(delimiterIndex + 1); namedArguments.push({ raw, formalName, negated, value, }); } else { throw new Error(`Unexpected an unknown argument: ${raw}`); } } else { positionalArguments.push(raw); } return [positionalArguments, namedArguments]; }, [[], []]); const outputHash = Object.assign({}, defaultHash); for (const { raw, formalName, negated, value, } of namedArguments) { const defaultValue = defaultHash[formalName]; if (typeof defaultValue === 'boolean') { const parsedValue = (0, parseBoolean_1.parseBoolean)(value, true); outputHash[formalName] = negated ? !parsedValue : parsedValue; } else if (typeof defaultValue === 'number') { if (negated) { if (value) { throw new Error(`Unexpected a value when using "no" name prefix: ${raw}`); } else { outputHash[formalName] = defaultHash[formalName]; } } else { outputHash[formalName] = value === undefined ? NaN : parseFloat(value); } } else if (typeof defaultValue === 'string') { if (negated) { if (value === undefined || outputHash[formalName] === value) { outputHash[formalName] = ''; } } else if (value === undefined) { throw new Error(`Expected a value: ${raw}`); } else { outputHash[formalName] = value; } } else if (Array.isArray(defaultValue)) { if (negated) { if (value === undefined) { outputHash[formalName] = []; } else { outputHash[formalName] = difference(outputHash[formalName], [value]); } } else { if (value === undefined) { throw new Error(`Expected a value: ${raw}`); } if (outputHash[formalName].includes(value)) { outputHash[formalName] = difference(outputHash[formalName], [value]); } outputHash[formalName] = [...outputHash[formalName], value]; } } } if (options?.exclusives) { const inputFormalNames = Array.from(new Set(namedArguments.map(({ formalName }) => formalName))); const formalNameToRaw = Object.fromEntries(namedArguments.map(({ formalName, raw }) => [formalName, raw])); for (const group of options.exclusives) { const intersections = intersect(group, inputFormalNames); if (intersections.length > 1) { throw new Error('Unexpected mutual exclusive arguments: ' + intersections.map(field => formalNameToRaw[field]).join(' ')); } } } return Object.assign(outputHash, positionalArguments, { length: positionalArguments.length }); function isKnownName(name) { return name in defaultHash; } } function difference(sourceList, subtractorList) { return sourceList.filter(item => !subtractorList.includes(item)); } function intersect(sourceList, comparingList) { return sourceList.filter(item => comparingList.includes(item)); }