import GitHubInfo from './GitHubInfo';
import program from 'commander';
import { getConfigFromFile } from './_utils.js';
/** Class creating a Commander program, managing the options passed via bash and config file. */
class Program {
constructor(props) {
const { programOptions, defaults } = this._consumeOptions(props.options);
this.name = props.name;
this.description = props.description;
this.examples = props.examples;
this.defaults = defaults;
this.program = this._programWithEvents(this._programWithOptions(program, programOptions), props.events)
.name(this.name)
.description(this.description)
.parse(props.argv);
this.options = Object.assign(
{},
getConfigFromFile(props.cwd, program.config),
this._getOptionsFromObject(this.program, this.defaults)
);
}
/**
* Initialise the module
*
* @since 0.10.0
* @public
*
* @return {Promise}
*/
async init() {
const options = await this._getEnvOptions();
this.options = this._filterObject(this._camelCaseObjectKeys(
Object.assign(
{},
this.defaults,
Object.assign({}, ...[].concat(options)),
this.options
)
));
return this.options;
}
/**
* Get informations from the local folder
*
* @since 0.10.0
* @private
*
* @return {Promise}
*/
_getEnvOptions() {
const githubInfo = new GitHubInfo();
const { username, repo } = this.options;
if (username && repo) {
return githubInfo.token;
}
return githubInfo.options;
}
/**
* Remove all the properties that have undefined values from an object
*
* @since 0.10.0
* @private
*
* @param {Object} object
*
* @return {Object}
*/
_filterObject(object) {
return Object.entries(object)
.filter(([key, value]) => value !== undefined)
.reduce((carry, [key, value]) => {
carry[key] = value;
return carry;
}, {});
}
/**
* Add all the given events to a program
*
* @since 0.10.0
* @private
*
* @param {Commander} program
* @param {Object} events
*
* @return {Commander}
*/
_programWithEvents(program, events) {
if (!events || !events.length) {
return program;
}
Object.entries(events).forEach(([event, action]) => {
program.on(event, action);
});
return program;
}
/**
* Add all the given options to a program
*
* @since 0.10.0
* @private
*
* @param {Commander} program
* @param {Array} options
*
* @return {Commander}
*/
_programWithOptions(program, options) {
options.forEach(({ name, description, action, defaultValue }) => program.option(...[name, description, action, defaultValue].filter(Boolean)));
return program;
}
/**
* Consume the options from the properties and provide get the defaults and the programOptions
*
* @since 0.10.0
* @private
*
* @param {Array} opts
*
* @return {Object}
*/
_consumeOptions(opts = []) {
if (!Array.isArray(opts)) {
return {
programOptions: [],
defaults: {}
};
}
const programOptions = opts.map(({ short, name, valueType, description, defaultValue, action }) => ({
name: short && name ? `${short}, --${name} ${valueType || ''}` : ' ',
description,
defaultValue,
action
}));
const defaults = this._camelCaseObjectKeys(
opts.reduce((carry, opt) => {
carry[opt.name] = opt.defaultValue;
return carry;
}, {})
);
return {
programOptions,
defaults
};
}
/**
* Extrapulate the options from a program
*
* @since 0.10.0
* @private
*
* @param {Object} defaults
*
* @return {Object}
*/
_getOptionsFromObject(object = {}, defaults = {}) {
if (typeof object !== 'object' || Array.isArray(object)) {
return {};
}
return Object.keys(defaults).reduce((carry, option) => {
if (object[option] && object[option] !== defaults[option]) {
carry[option] = object[option];
}
return carry;
}, {});
}
/**
* Converts all Object values to camel case
*
* @param {Object} object
*
* @return {Object}
*/
_camelCaseObjectKeys(object = {}) {
if (typeof object !== 'object' || Array.isArray(object)) {
return {};
}
return Object.entries(object).reduce((carry, [key, value]) => {
carry[this._dashToCamelCase(key)] = value;
return carry;
}, {});
}
/**
* Transforms a dasherize string into a camel case one.
*
* @since 0.3.2
* @private
*
* @param {string} value The dasherize string
*
* @return {string} The camel case string
*/
_dashToCamelCase(value = '') {
if (typeof value !== 'string') {
return '';
}
return value
.replace(/-([a-z])/g, (match) => match[1].toUpperCase());
}
}
export default Program;