commanding
Version:
A simple yet practical command-Line application framework, written in TypeScript.
187 lines (186 loc) • 7.78 kB
JavaScript
;
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;