import _ from 'lodash'; import commandSchema from './commands-schema.js'; import serviceOptions from './commands-options-schema.js'; import logDeprecation from '../utils/log-deprecation.js'; /** * This module exports a function that processes loaded plugins and their * configurations, mapping out their commands and options into a commands Map. * It imports AWS service commands and common options, and merges them with * the commands and options from the loaded plugins. It also handles missing * option types by logging a deprecation warning. * * @param {Array} loadedPlugins - An array of loaded plugin objects. * @param {Object} configuration - The configuration object for the plugins. * @returns {Map} A Map object of commands and their options. */ export default (loadedPlugins, { configuration }) => { const commands = new Map(commandSchema); const missingOptionTypes = new Map(); const commonOptions = serviceOptions; commands.commonOptions = commonOptions; /** * Recursively processes a configuration object for a plugin, mapping out its * commands and their options. It skips commands of type 'entrypoint', and for * other commands, it creates or updates a schema with details like usage, * lifecycle events, and options. If an option is missing the 'type' property, * it is added to the `missingOptionTypes` set for later processing. * * @param {Object} loadedPlugin - The loaded plugin object. * @param {Object} config - The configuration object for the plugin. * @param {string} commandPrefix - The prefix for the command (default is ''). */ const resolveCommands = (loadedPlugin, config, commandPrefix = '') => { if (!config.commands) return; for (const [commandName, commandConfig] of Object.entries(config.commands)) { if (commandConfig.type === 'entrypoint') continue; const fullCommandName = `${commandPrefix}${commandName}`; if (commandConfig.type !== 'container') { const schema = commands.has(fullCommandName) ? _.merge({}, commands.get(fullCommandName)) : { usage: commandConfig.usage, serviceDependencyMode: 'required', isExtension: true, sourcePlugin: loadedPlugin, isHidden: commandConfig.isHidden, noSupportNotice: commandConfig.noSupportNotice, options: {}, }; if (commandConfig.lifecycleEvents) schema.lifecycleEvents = commandConfig.lifecycleEvents; if (commandConfig.options) { for (const [optionName, optionConfig] of Object.entries(commandConfig.options)) { if (!schema.options[optionName]) { schema.options[optionName] = optionConfig; if (!optionConfig.type) { if (!missingOptionTypes.has(loadedPlugin)) { missingOptionTypes.set(loadedPlugin, new Set()); } missingOptionTypes.get(loadedPlugin).add(optionName); } } } } // Put common options to end of index for (const optionName of Object.keys(commonOptions)) delete schema.options[optionName]; Object.assign(schema.options, commonOptions); commands.set(fullCommandName, schema); } resolveCommands(loadedPlugin, commandConfig, `${fullCommandName} `); } }; for (const loadedPlugin of loadedPlugins) resolveCommands(loadedPlugin, loadedPlugin); if (missingOptionTypes.size) { logDeprecation( 'CLI_OPTIONS_SCHEMA_V3', 'CLI options definitions were upgraded with "type" property (which could be one of "string", "boolean", "multiple"). ' + 'Below listed plugins do not predefine type for introduced options:\n' + ` - ${Array.from( missingOptionTypes, ([plugin, optionNames]) => `${plugin.constructor.name} for "${Array.from(optionNames).join('", "')}"` ).join('\n - ')}\n` + 'Please report this issue in plugin issue tracker.\n' + 'Starting with next major release, this will be communicated with a thrown error.', { serviceConfig: configuration } ); } return commands; };