appcenter-cli
Version:
Command line tool for Visual Studio App Center
179 lines (178 loc) • 7.97 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.help = exports.defaultValue = exports.required = exports.name = exports.position = exports.common = exports.hasArg = exports.longName = exports.shortName = exports.getClassHelpText = exports.getPositionalOptionsDescription = exports.getOptionsDescription = exports.classHelpTextKey = void 0;
const util_1 = require("util");
const lodash_1 = require("lodash");
const debug = require("debug")("appcenter-cli:util:commandline:option-decorators");
const optionDescriptionKey = Symbol("OptionParameters");
const positionalDescriptionKey = Symbol("PositionalParameters");
const unknownRequiredsKey = Symbol("UnknownRequireds");
const unknownDefaultValueKey = Symbol("UnknownDefaultValues");
const unknownHelpTextKey = Symbol("UnknownDescription");
exports.classHelpTextKey = Symbol("ClassHelpText");
function getOptionsDescription(target) {
// option description can be overridden in children class
function getRecursive(accumulator, target) {
if (target) {
getRecursive(accumulator, Object.getPrototypeOf(target));
}
else {
return accumulator;
}
if (target.hasOwnProperty(optionDescriptionKey)) {
lodash_1.assign(accumulator, target[optionDescriptionKey]);
}
return accumulator;
}
return getRecursive({}, target);
}
exports.getOptionsDescription = getOptionsDescription;
function getLocalOptionsDescription(target) {
if (!target.hasOwnProperty(optionDescriptionKey)) {
target[optionDescriptionKey] = {};
}
return target[optionDescriptionKey];
}
function getPositionalOptionsDescription(target) {
function getRecursive(accumulator, target) {
if (!target || !target.hasOwnProperty(positionalDescriptionKey)) {
return accumulator;
}
let newOpts = [];
if (target.hasOwnProperty(positionalDescriptionKey)) {
newOpts = target[positionalDescriptionKey];
}
return getRecursive(newOpts.concat(accumulator), Object.getPrototypeOf(target));
}
return getRecursive([], target);
}
exports.getPositionalOptionsDescription = getPositionalOptionsDescription;
function getLocalPositionalOptionsDescription(target) {
if (!target.hasOwnProperty(positionalDescriptionKey)) {
target[positionalDescriptionKey] = [];
}
return target[positionalDescriptionKey];
}
function getClassHelpText(target) {
return target[exports.classHelpTextKey];
}
exports.getClassHelpText = getClassHelpText;
function updateUnknownRequireds(option, propertyKey, proto) {
if (proto[unknownRequiredsKey] && proto[unknownRequiredsKey].has(propertyKey)) {
option.required = true;
proto[unknownRequiredsKey].delete(propertyKey);
}
}
function updateUnknownDefaultValues(option, propertyKey, proto) {
if (proto[unknownDefaultValueKey] && proto[unknownDefaultValueKey].has(propertyKey)) {
option.defaultValue = proto[unknownDefaultValueKey].get(propertyKey);
proto[unknownDefaultValueKey].delete(propertyKey);
}
}
function updateUnknownHelpTexts(option, propertyKey, proto) {
if (proto[unknownHelpTextKey] && proto[unknownHelpTextKey].has(propertyKey)) {
option.helpText = proto[unknownHelpTextKey].get(propertyKey);
proto[unknownHelpTextKey].delete(propertyKey);
}
}
function updateUnknowns(option, propertyKey, proto) {
updateUnknownRequireds(option, propertyKey, proto);
updateUnknownDefaultValues(option, propertyKey, proto);
updateUnknownHelpTexts(option, propertyKey, proto);
}
function makeStringDecorator(descriptionFieldName) {
return function decoratorBuilder(name) {
return function paramDecorator(proto, propertyKey) {
const optionsDescription = getLocalOptionsDescription(proto);
const option = optionsDescription[propertyKey] || {};
option[descriptionFieldName] = name;
updateUnknowns(option, propertyKey, proto);
optionsDescription[propertyKey] = option;
};
};
}
function makeBoolDecorator(descriptionFieldName) {
return function paramDecorator(proto, propertyKey) {
const optionsDescription = getLocalOptionsDescription(proto);
const option = optionsDescription[propertyKey] || {};
option[descriptionFieldName] = true;
updateUnknowns(option, propertyKey, proto);
optionsDescription[propertyKey] = option;
};
}
// Short and long name decorators
exports.shortName = makeStringDecorator("shortName");
exports.longName = makeStringDecorator("longName");
exports.hasArg = makeBoolDecorator("hasArg");
exports.common = makeBoolDecorator("common");
function makePositionalDecorator(descriptionFieldName) {
return function positionalDecoratorBuilder(value) {
return function positionalDecorator(proto, propertyKey) {
const optionsDescription = getLocalPositionalOptionsDescription(proto);
let option = optionsDescription.find((opt) => opt.propertyName === propertyKey);
if (option === undefined) {
option = { propertyName: propertyKey, name: "", position: -1 };
optionsDescription.push(option);
updateUnknowns(option, propertyKey, proto);
}
option[descriptionFieldName] = value;
};
};
}
exports.position = makePositionalDecorator("position");
exports.name = makePositionalDecorator("name");
//
// Logic or handling decorators that apply to both positional and
// flag arguments. Needs to be slightly special since we may not
// know which one the parameter is until a later decorator runs.
//
function saveDecoratedValue(proto, propertyKey, descriptionProperty, value, unknownFieldKey) {
const flagOpts = getLocalOptionsDescription(proto);
if (flagOpts.hasOwnProperty(propertyKey.toString())) {
flagOpts[propertyKey.toString()][descriptionProperty] = value;
return;
}
const positionalOpts = getLocalPositionalOptionsDescription(proto);
const opt = positionalOpts.find((opt) => opt.propertyName === propertyKey);
if (opt !== undefined) {
opt[descriptionProperty] = value;
return;
}
const unknownValues = (proto[unknownFieldKey] = proto[unknownFieldKey] || new Map());
unknownValues.set(propertyKey.toString(), value);
}
// Required is special, since it has to work on both flag and positional parameters.
// If it's the first decorator, stick name in a set to check later once we know
// which one it is
function required(proto, propertyKey) {
saveDecoratedValue(proto, propertyKey, "required", true, unknownRequiredsKey);
}
exports.required = required;
// DefaultValue is also special, since it has to work with both as well. Same
// basic logic
function defaultValue(value) {
return function defaultValueDecorator(proto, propertyKey) {
saveDecoratedValue(proto, propertyKey, "defaultValue", value, unknownDefaultValueKey);
};
}
exports.defaultValue = defaultValue;
// Decorator factory to give a consolidated helptext API across class & parameter
function help(helpText) {
return function helpDecoratorFactory(...args) {
debug(`@help decorator called with ${args.length} arguments: ${util_1.inspect(args)}`);
if (args.length === 1) {
const ctor = args[0];
ctor[exports.classHelpTextKey] = helpText;
return ctor;
}
// Typescript docs are incorrect - property decorators get three args, and the last one is
// undefined
if (args.length === 3 && typeof args[0] === "object" && args[2] === undefined) {
const proto = args[0];
const propertyName = args[1];
return saveDecoratedValue(proto, propertyName, "helpText", helpText, unknownHelpTextKey);
}
throw new Error("@help not valid in this location");
};
}
exports.help = help;