UNPKG

commanding

Version:

A simple yet practical command-Line application framework, written in TypeScript.

187 lines (186 loc) 7.78 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const lodash_1 = __importDefault(require("lodash")); const path_1 = __importDefault(require("path")); const InvalidInput_1 = require("../errors/InvalidInput"); const MissingOption_1 = require("../errors/MissingOption"); const fake_1 = require("../sanitizers/fake"); const MissingArgument_1 = require("../errors/MissingArgument"); const SINGLE_QUOTE = '\''; const DOUBLE_QUOTE = '"'; exports.isStartWithQuote = lodash_1.default.flow([ lodash_1.default.overSome([ str => lodash_1.default.endsWith(str, SINGLE_QUOTE), str => lodash_1.default.endsWith(str, DOUBLE_QUOTE), ]), ]); exports.isEndWithQuote = lodash_1.default.flow([ lodash_1.default.overSome([ str => lodash_1.default.endsWith(str, SINGLE_QUOTE), str => lodash_1.default.endsWith(str, DOUBLE_QUOTE), ]), ]); function parseOptionStr(option) { const matched = /^(--?[^-= ][^= ]*?)(?:(?:=| )(.+)?)?$/i.exec(option); if (matched === null) { throw new InvalidInput_1.InvalidInput(`Invalid options "${option}"`); } const [, name, value] = matched; return (value === undefined) ? [name, true] : [name, value]; } exports.parseOptionStr = parseOptionStr; function parseArgv(argv) { const [options, args] = lodash_1.default.partition(argv, (part) => lodash_1.default.startsWith(part, '-')); const parsedArgs = args.map(arg => arg.trim()); const parsedOptions = options .map(option => option.trim()) .reduce((carry, option, index) => { const [name, value] = parseOptionStr(option); const previousContents = lodash_1.default.has(carry, name) ? [...carry[name]] : []; const currentContent = { value, order: index }; return Object.assign(Object.assign({}, carry), { [name]: [...previousContents, currentContent] }); }, {}); return [parsedArgs, parsedOptions]; } exports.parseArgv = parseArgv; function extractExecutableNameFromArgv(argv) { return path_1.default.basename(argv[1]); } exports.extractExecutableNameFromArgv = extractExecutableNameFromArgv; function mergeParsedOptionContentValues(contents) { return lodash_1.default.flatten(contents.map(content => content.value)); } exports.mergeParsedOptionContentValues = mergeParsedOptionContentValues; function getLatestValueFromParsedOptionContents(contents) { const latestContent = lodash_1.default.first(lodash_1.default.sortBy(contents, ['order'], 'desc')); return lodash_1.default.get(latestContent, 'value'); } exports.getLatestValueFromParsedOptionContents = getLatestValueFromParsedOptionContents; function splitCsvStr(str) { return `${str}`.split(','); } exports.splitCsvStr = splitCsvStr; function makeSplitAndSanitizeFunc(isCsv, optionSanitizer) { const sanitizer = optionSanitizer ? optionSanitizer : new fake_1.FakeSanitizer(); const splitAndSanitize = (parsedOptionValue) => { if (isCsv) { return splitCsvStr(parsedOptionValue).map(value => sanitizer.sanitize(value)); } return sanitizer.sanitize(parsedOptionValue); }; return (parsedOptionValue) => { if (parsedOptionValue === undefined) { return parsedOptionValue; } return Array.isArray(parsedOptionValue) ? parsedOptionValue.map(splitAndSanitize) : splitAndSanitize(parsedOptionValue); }; } exports.makeSplitAndSanitizeFunc = makeSplitAndSanitizeFunc; function mapArguments(parsedArgs, argRequirements) { return lodash_1.default.sortBy(argRequirements, 'position', 'asc') .map(requirement => { let value = lodash_1.default.get(parsedArgs, requirement.position); if (value === undefined) { if (requirement.required) { throw new MissingArgument_1.MissingArgument(requirement); } if (requirement.defaultValue) { value = requirement.defaultValue; } } if (requirement.sanitizer) { value = requirement.sanitizer.sanitize(value); } return [requirement, value]; }) .reduce((mapped, [requirement, value]) => { mapped[requirement.name] = value; return mapped; }, {}); } exports.mapArguments = mapArguments; function mapOptions(parsedOptions, optionRequirements) { const mapped = {}; return optionRequirements .map(requirement => { const mergedContents = lodash_1.default.merge((requirement.shorthand ? lodash_1.default.get(parsedOptions, requirement.shorthand, []) : []), (requirement.longhand ? lodash_1.default.get(parsedOptions, requirement.longhand, []) : [])); let parsedOptionValue = requirement.repeatable ? mergeParsedOptionContentValues(mergedContents) : getLatestValueFromParsedOptionContents(mergedContents); if (parsedOptionValue === undefined) { if (requirement.required) { throw new MissingOption_1.MissingOption(requirement); } if (requirement.defaultValue) { parsedOptionValue = requirement.defaultValue; } } let mappedOptionValue = makeSplitAndSanitizeFunc(requirement.csv, requirement.sanitizer)(parsedOptionValue); return [ requirement, mappedOptionValue, ]; }) // .filter((pair): pair is Pair => pair !== undefined) .reduce((mapped, [requirement, value]) => { if (requirement.shorthand) { mapped[requirement.shorthand] = value; } if (requirement.longhand) { mapped[requirement.longhand] = value; } return mapped; }, {}); } exports.mapOptions = mapOptions; function isShorthandOptionName(name) { return /^-[^-]$/.test(name); } exports.isShorthandOptionName = isShorthandOptionName; function isLonghandOptionName(name) { return /^--[^-]+$/.test(name); } exports.isLonghandOptionName = isLonghandOptionName; function normalizeOptionNames(inputFullName) { if (inputFullName.trim() === '') { throw new InvalidInput_1.InvalidInput(`You should specify the option name.`); } const names = splitCsvStr(inputFullName.trim()) .map(name => name.trim()); if (names.length > 2) { throw new InvalidInput_1.InvalidInput(`Option can only have most two names.`); } const normalized = names.reduce((normalized, name) => { if (isShorthandOptionName(name)) { if (normalized.shorthand !== undefined) { throw new InvalidInput_1.InvalidInput(`You can not assign more than one shorthand-name for a option.`); } normalized.shorthand = name; } if (isLonghandOptionName(name)) { if (normalized.longhand !== undefined) { throw new InvalidInput_1.InvalidInput(`You can not assign more than one longhand-name for a option.`); } normalized.longhand = name; } return normalized; }, { shorthand: undefined, longhand: undefined }); if (lodash_1.default.valuesIn(normalized).every(lodash_1.default.isUndefined)) { throw new InvalidInput_1.InvalidInput(`Option name error: "${inputFullName}", should be "-o", "--option" or "-o, --option".`); } const fullName = [normalized.shorthand, normalized.longhand] .filter(name => name !== undefined).join(', '); return { fullName, shorthand: normalized.shorthand, longhand: normalized.longhand, }; } exports.normalizeOptionNames = normalizeOptionNames;