UNPKG

@thisismanta/pessimist

Version:

This is a Node.js library that helps derive `process.argv` into a first-hand-**TypeScript** value-strict object with very minimal configurations.

193 lines (192 loc) 8.29 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 logicalHash = {}; for (const name in defaultHash) { const formalName = name; if (name.length === 1 && name !== '-') { logicalHash['-' + name] = { formalName, negated: false }; } else { const kebabName = (0, kebabCase_1.default)(name); logicalHash['--' + kebabName] = { formalName, negated: false }; if (kebabName.startsWith('no-')) { logicalHash['--' + kebabName.substring(3)] = { formalName, negated: true }; } else { logicalHash['--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 !== '-') { logicalHash[_a = '-' + aliasName] ?? (logicalHash[_a] = { formalName, negated }); } else { const kebabName = (0, kebabCase_1.default)(aliasName); logicalHash[_b = '--' + kebabName] ?? (logicalHash[_b] = { formalName, negated }); if (kebabName.startsWith('no-')) { logicalHash[_c = '--' + kebabName.substring(3)] ?? (logicalHash[_c] = { formalName, negated: !negated }); } else { logicalHash[_d = '--no-' + kebabName] ?? (logicalHash[_d] = { formalName, negated: !negated }); } } } } const namedArguments = []; const positionalArguments = []; for (const raw of inputs) { if (/^-+$/.test(raw)) { continue; } else if (raw.startsWith('--')) { const delimiter = raw.indexOf('='); const actualName = delimiter === -1 ? raw : raw.substring(0, delimiter); const kebabName = '--' + (0, kebabCase_1.default)(actualName); if (kebabName in logicalHash === false) { throw new Error(`Unexpected an unknown argument: ${raw}`); } const { formalName, negated } = logicalHash[kebabName]; const value = delimiter === -1 ? undefined : raw.substring(delimiter + 1); namedArguments.push({ raw, formalName, negated, value, }); } else if (raw.startsWith('-')) { const delimiter = raw.indexOf('='); if (delimiter === -1) { for (const name of raw.substring(1).split('')) { const kebabName = '-' + name; if (kebabName in logicalHash === false) { throw new Error(`Unexpected an unknown argument: ${raw}`); } const { formalName, negated } = logicalHash[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 (delimiter === 2) { const actualName = raw.substring(0, delimiter); if (actualName in logicalHash === false) { throw new Error(`Unexpected an unknown argument: ${raw}`); } const { formalName, negated } = logicalHash[actualName]; const value = raw.substring(delimiter + 1); namedArguments.push({ raw, formalName, negated, value, }); } else { throw new Error(`Unexpected an unknown argument: ${raw}`); } } else { positionalArguments.push(raw); } } 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)); }