src/cli/index.js
import 'source-map-support/register';
import minimist from 'minimist';
import { isString } from 'lodash';
import { execute } from './execute';
import { getAbsolutePath } from '../helpers';
import { validate } from '../validation';
import { merge, appendConfig } from '../configuration';
import buildDocumentationObject from '../documentation/build-documentation-object';
import { getApplicationConfig } from '../configuration/helpers';
import {
buildCompleteConfig,
generateCommandsDocumentation,
generateCommandDocumentation,
parseOptions,
getMappings,
parseArguments,
getSuggestions
} from './helpers';
import { error as styleError } from '../helpers/style';
/**
* Invokes the Roc cli.
*
* @param {{version: string, name: string}} info - Information about the cli.
* @param {rocConfig} initalConfig - The inital configuration, will be merged with the selected extensions and
* application.
* @param {rocMetaConfig} initalMeta - The inital meta configuration, will be merged with the selected extensions.
* @param {string[]} [args=process.argv] - From where it should parse the arguments.
*
* @returns {undefined}
*/
export function runCli(info = {version: 'Unknown', name: 'Unknown'}, initalConfig, initalMeta, args = process.argv) {
const {_, h, help, d, debug, v, version, c, config, D, directory, ...restArgs} = minimist(args.slice(2));
// The first should be our command if there is one
const [command, ...options] = _;
// If version is selected output that and stop
if (version || v) {
return console.log(info.version);
}
// Possibe to set a command in debug mode
const debugEnabled = (debug || d) ? true : false;
// Get the application configuration path
const applicationConfigPath = config || c;
// Get the directory Path
const directoryPath = getAbsolutePath(directory || D);
// Build the complete config object
const applicationConfig = getApplicationConfig(applicationConfigPath, directoryPath, debugEnabled);
let { extensionConfig, config: configObject, meta: metaObject } =
buildCompleteConfig(debugEnabled, initalConfig, initalMeta, applicationConfig, undefined, directoryPath);
// If we have no command we will display some help information about all possible commands
if (!command) {
return console.log(generateCommandsDocumentation(extensionConfig, metaObject));
}
// If the command does not exist show error
// Will ignore application configuration
if (!extensionConfig.commands || !extensionConfig.commands[command]) {
console.log(styleError('Invalid command'), '\n');
return console.log(getSuggestions([command], Object.keys(extensionConfig.commands)), '\n');
}
// Show command help information if requested
// Will ignore application configuration
if (help || h) {
return console.log(generateCommandDocumentation(extensionConfig, metaObject, command, info.name));
}
const parsedOptions = parseOptions(command, metaObject.commands, options);
// Only parse arguments if the command accepts it
if (metaObject.commands[command] && metaObject.commands[command].settings) {
// Get config from application and only parse options that this command cares about.
const filter = metaObject.commands[command].settings === true ? [] : metaObject.commands[command].settings;
const documentationObject = buildDocumentationObject(configObject.settings, metaObject.settings, filter);
const configuration = parseArguments(restArgs, getMappings(documentationObject));
configObject = merge(configObject, {
settings: configuration
});
// Validate configuration
validate(configObject.settings, metaObject.settings, metaObject.commands[command].settings);
}
// Set the configuration object
appendConfig(configObject);
// If string run as shell command
if (isString(configObject.commands[command])) {
return execute(configObject.commands[command]);
}
// Run the command
return configObject.commands[command]({
debug: debugEnabled,
configObject,
metaObject,
extensionConfig,
parsedOptions
});
}