diff --git a/README.md b/README.md index b3f5a12eea..647c1151ee 100644 --- a/README.md +++ b/README.md @@ -55,6 +55,16 @@ Usage on with `@edx/brand`: Note that including fonts will affect performance. In some applications may choose not to load the custom font to keep it highly performant. +## Paragon CLI + +The Paragon CLI (Command Line Interface) is a tool that provides various utility commands to automate actions within the Open edX environment. + +### Available Commands + +- `paragon install-theme [theme]`: Installs the specific @edx/brand package. + +Use `paragon help` to see more information. + ## Getting Help Please reach out to the Paragon Working Group (PWG): diff --git a/bin/paragon-scripts.js b/bin/paragon-scripts.js index acf1252236..47ef258f0b 100755 --- a/bin/paragon-scripts.js +++ b/bin/paragon-scripts.js @@ -1,10 +1,52 @@ #!/usr/bin/env node const chalk = require('chalk'); const themeCommand = require('../lib/install-theme'); +const helpCommand = require('../lib/help'); + +const HELP_COMMAND = 'help'; -// command: executor function const COMMANDS = { - 'install-theme': themeCommand, + /** + *'command-name': { + * executor: executorFunc, + * + * ********** Block for help command start ********** + * description: 'Command description', + * parameters: [ + * { + * name: 'paramName', + * description: 'paramDescription', + * defaultValue: 'paramDefaultValue', + * required: true/false, + * }, + * ... + * ], + * options: [ + * { + * name: '--optionName', + * description: 'optionDescription', + * }, + * ... + * ], + * ********** Block for help command end ********** + *}, + */ + 'install-theme': { + executor: themeCommand, + description: 'Installs the specific @edx/brand package.', + parameters: [ + { + name: 'theme', + description: 'The @edx/brand package to install.', + defaultValue: '@edx/brand-openedx@latest', + required: false, + }, + ], + }, + help: { + executor: helpCommand, + description: 'Displays help for available commands.', + }, }; (async () => { @@ -13,12 +55,17 @@ const COMMANDS = { if (!executor) { // eslint-disable-next-line no-console - console.log(chalk.red.bold('Unknown command. Usage: paragon ')); + console.log(chalk.red.bold('Unknown command. Usage: paragon .')); + return; + } + + if (command === HELP_COMMAND) { + helpCommand(COMMANDS); return; } try { - await executor(); + await executor.executor(); } catch (error) { // eslint-disable-next-line no-console console.error(chalk.red.bold('An error occurred:', error.message)); diff --git a/lib/help.js b/lib/help.js new file mode 100644 index 0000000000..9491995a18 --- /dev/null +++ b/lib/help.js @@ -0,0 +1,57 @@ +/* eslint-disable no-console */ +const chalk = require('chalk'); + +const DESCRIPTION_PAD = 20; + +/** + * Pads a description string to align with a specified offset string. + * + * @param {string} description - The description to pad. + * @param {string} offsetString - The offset string that the description should align with. + * @returns {string} - The padded description. + */ +function padLeft(description, offsetString) { + // Calculate the necessary padding based on the offsetString length + const padding = ' '.repeat(Math.max(0, DESCRIPTION_PAD - offsetString.length)); + return `${padding}${description}`; +} + +/** + * Displays a help message for available commands, including descriptions, parameters, and options. + * + * @param {Object} commands - An object containing information about available commands. + */ +function helpCommand(commands) { + console.log(chalk.yellow.bold('Paragon Help')); + console.log(); + console.log('Available commands:'); + console.log(); + + Object.entries(commands).forEach(([command, { parameters, description, options }]) => { + console.log(` ${chalk.green.bold(command)}`); + if (description) { + console.log(` ${description}`); + } + + if (parameters && parameters.length > 0) { + console.log(` ${chalk.cyan('Parameters: ')}`); + parameters.forEach(parameter => { + const requiredStatus = parameter.required ? 'Required' : 'Optional'; + const formattedDescription = padLeft(parameter.description, parameter.name); + console.log(` ${parameter.name}${formattedDescription} (${requiredStatus}, Default: ${parameter.defaultValue || 'None'})`); + }); + } + + if (options && options.length > 0) { + console.log(` ${chalk.cyan('Options: ')}`); + options.forEach(option => { + const formattedDescription = padLeft(option.description, option.name); + console.log(` ${option.name}${formattedDescription}`); + }); + } + + console.log(); + }); +} + +module.exports = helpCommand; diff --git a/lib/install-theme.js b/lib/install-theme.js index edc3c09e00..a7b408c24b 100644 --- a/lib/install-theme.js +++ b/lib/install-theme.js @@ -1,6 +1,11 @@ const inquirer = require('inquirer'); const childProcess = require('child_process'); +/** + * Prompts the user to enter the @edx/brand package they want to install. + * + * @returns {Promise} - A Promise that resolves to an object containing the user's input. + */ function promptUserForTheme() { return inquirer.prompt([ { @@ -12,6 +17,11 @@ function promptUserForTheme() { ]); } +/** + * Installs a specified @edx/brand package. + * + * @param {string} theme - The @edx/brand package to install. + */ function installTheme(theme) { const version = theme ? `npm:${theme}` : ''; @@ -20,9 +30,19 @@ function installTheme(theme) { childProcess.execSync(installCommand, { stdio: 'inherit' }); } +/** + * Command handler for installing an @edx/brand package. + */ async function themeCommand() { - const answers = await promptUserForTheme(); - installTheme(answers.theme); + // Check if the user passed a theme parameter as a command-line argument + const userPassedThemeParameter = process.argv.length === 4; + if (userPassedThemeParameter) { + const providedTheme = process.argv[3]; + installTheme(providedTheme); + } else { + const answers = await promptUserForTheme(); + installTheme(answers.theme); + } } module.exports = themeCommand;