diff --git a/packages/amplify-category-analytics/src/commands/analytics.ts b/packages/amplify-category-analytics/src/commands/analytics.ts index 7f50c744922..77b51ffe9cf 100644 --- a/packages/amplify-category-analytics/src/commands/analytics.ts +++ b/packages/amplify-category-analytics/src/commands/analytics.ts @@ -1,5 +1,5 @@ import { $TSAny, $TSContext } from 'amplify-cli-core'; -import { printer } from 'amplify-prompts'; +import { run as runHelp } from './analytics/help'; export { run as analyticsPush } from './analytics/push'; export const name = 'analytics'; @@ -9,36 +9,12 @@ export const name = 'analytics'; * @param context amplify cli context */ export const run = async (context: $TSContext): Promise<$TSAny> => { + if (context.parameters.options.help) { + return runHelp(context); + } if (/^win/.test(process.platform)) { const { run: runCommand } = await import(`./${name}/${context.parameters.first}`); return runCommand(context); } - const header = `amplify ${name} `; - - const commands = [ - { - name: 'add', - description: `Takes you through a CLI flow to add an ${name} resource to your local backend`, - }, - { - name: 'update', - description: `Takes you through steps in the CLI to update an ${name} resource`, - }, - { - name: 'push', - description: `Provisions only ${name} cloud resources with the latest local developments`, - }, - { - name: 'remove', - description: `Removes ${name} resource from your local backend. The resource is removed from the cloud on the next push command.`, - }, - { - name: 'console', - description: `Opens the web console for the ${name} category`, - }, - ]; - - context.amplify.showHelp(header, commands); - printer.blankLine(); return context; }; diff --git a/packages/amplify-category-analytics/src/commands/analytics/help.ts b/packages/amplify-category-analytics/src/commands/analytics/help.ts new file mode 100644 index 00000000000..adb7dc15830 --- /dev/null +++ b/packages/amplify-category-analytics/src/commands/analytics/help.ts @@ -0,0 +1,5 @@ +import { $TSContext, runHelp, commandsInfo } from 'amplify-cli-core'; + +export const run = (context: $TSContext) => { + runHelp(context, commandsInfo); +}; diff --git a/packages/amplify-category-auth/src/commands/auth.ts b/packages/amplify-category-auth/src/commands/auth.ts index 6a6fdebc938..9fb909ab71b 100644 --- a/packages/amplify-category-auth/src/commands/auth.ts +++ b/packages/amplify-category-auth/src/commands/auth.ts @@ -1,6 +1,7 @@ import { $TSAny, $TSContext } from 'amplify-cli-core'; import { printer } from 'amplify-prompts'; import * as path from 'path'; +import { run as runHelp } from './auth/help'; export const name = 'auth'; @@ -15,42 +16,14 @@ type AuthCommandType = { * @returns auth command response */ export const run = async (context: $TSContext): Promise<$TSAny> => { + if (context.parameters.options.help) { + return runHelp(context); + } try { const { run: authRun } = await import(path.join('.', name, context.parameters.first)); return authRun(context); } catch (err) { - const header = `amplify ${name} `; - - const commands: AuthCommandType[] = [ - { - name: 'add', - description: `Takes you through a CLI flow to add an ${name} resource to your local backend`, - }, - { - name: 'import', - description: `Takes you through a CLI flow to import an existing ${name} resource to your local backend`, - }, - { - name: 'push', - description: `Provisions only ${name} cloud resources with the latest local developments`, - }, - { - name: 'remove', - description: `Removes the ${name} resource from your local backend which would be removed from the cloud on the next push command`, - }, - { - name: 'update', - description: `Updates the ${name} resource from your local backend.`, - }, - { - name: 'console', - description: `Opens the web console for the ${name} category`, - }, - ]; - - context.amplify.showHelp(header, commands); - printer.blankLine(); + printer.error('Command not found'); } - return undefined; }; diff --git a/packages/amplify-category-auth/src/commands/auth/help.ts b/packages/amplify-category-auth/src/commands/auth/help.ts new file mode 100644 index 00000000000..adb7dc15830 --- /dev/null +++ b/packages/amplify-category-auth/src/commands/auth/help.ts @@ -0,0 +1,5 @@ +import { $TSContext, runHelp, commandsInfo } from 'amplify-cli-core'; + +export const run = (context: $TSContext) => { + runHelp(context, commandsInfo); +}; diff --git a/packages/amplify-category-function/src/commands/function.ts b/packages/amplify-category-function/src/commands/function.ts index b7d52790387..2daa69195e3 100644 --- a/packages/amplify-category-function/src/commands/function.ts +++ b/packages/amplify-category-function/src/commands/function.ts @@ -1,8 +1,12 @@ import { categoryName } from '../constants'; +import { run as runHelp } from './function/help'; module.exports = { name: categoryName, run: async context => { + if (context.parameters.options.help) { + return runHelp(context); + } if (/^win/.test(process.platform)) { try { const { run } = require(`./${categoryName}/${context.parameters.first}`); @@ -11,38 +15,5 @@ module.exports = { context.print.error('Command not found'); } } - const header = `amplify ${categoryName} `; - - const commands = [ - { - name: 'add', - description: `Takes you through a CLI flow to add a ${categoryName} resource to your local backend`, - }, - { - name: 'update', - description: `Takes you through a CLI flow to update an existing ${categoryName} resource`, - }, - { - name: 'push', - description: `Provisions only ${categoryName} cloud resources with the latest local developments`, - }, - { - name: 'remove', - description: `Removes ${categoryName} resource from your local backend which would be removed from the cloud on the next push command`, - }, - { - name: 'build', - description: 'Builds all the functions in the project (does an npm install on the functions src directory)', - }, - { - name: 'console', - description: `Opens the web console for the ${categoryName} category`, - }, - ]; - - context.amplify.showHelp(header, commands); - - context.print.info(''); - return undefined; }, }; diff --git a/packages/amplify-category-function/src/commands/function/help.ts b/packages/amplify-category-function/src/commands/function/help.ts new file mode 100644 index 00000000000..adb7dc15830 --- /dev/null +++ b/packages/amplify-category-function/src/commands/function/help.ts @@ -0,0 +1,5 @@ +import { $TSContext, runHelp, commandsInfo } from 'amplify-cli-core'; + +export const run = (context: $TSContext) => { + runHelp(context, commandsInfo); +}; diff --git a/packages/amplify-category-geo/src/commands/geo/help.ts b/packages/amplify-category-geo/src/commands/geo/help.ts index 0f9a36434da..adb7dc15830 100644 --- a/packages/amplify-category-geo/src/commands/geo/help.ts +++ b/packages/amplify-category-geo/src/commands/geo/help.ts @@ -1,36 +1,5 @@ -import { $TSContext } from 'amplify-cli-core'; -import { category } from '../../constants'; -import { printer } from 'amplify-prompts'; +import { $TSContext, runHelp, commandsInfo } from 'amplify-cli-core'; -export const name = category; - -export const run = async (context: $TSContext) => { - const header = `amplify ${category} `; - - const commands = [ - { - name: 'add', - description: `Takes you through a CLI flow to add a ${category} resource to your local backend`, - }, - { - name: 'update', - description: `Takes you through steps in the CLI to update a ${category} resource`, - }, - { - name: 'push', - description: `Provisions only ${category} cloud resources with the latest local developments`, - }, - { - name: 'remove', - description: `Removes ${category} resource from your local backend. The resource is removed from the cloud on the next push command.`, - }, - { - name: 'console', - description: `Opens the web console for the ${category} category`, - }, - ]; - - context.amplify.showHelp(header, commands); - - printer.blankLine(); +export const run = (context: $TSContext) => { + runHelp(context, commandsInfo); }; diff --git a/packages/amplify-category-interactions/src/commands/interactions.js b/packages/amplify-category-interactions/src/commands/interactions.js index e5b6cc70bee..018e29c4afb 100644 --- a/packages/amplify-category-interactions/src/commands/interactions.js +++ b/packages/amplify-category-interactions/src/commands/interactions.js @@ -1,31 +1,12 @@ +import { run as runHelp } from './interactions/help'; + const featureName = 'interactions'; module.exports = { name: featureName, run: async context => { - const header = `amplify ${featureName} `; - - const commands = [ - { - name: 'add', - description: `Takes you through a CLI flow to add an ${featureName} resource to your local backend`, - }, - { - name: 'update', - description: `Takes you through a CLI flow to update an ${featureName} resource`, - }, - { - name: 'push', - description: `Provisions only ${featureName} cloud resources with the latest local developments`, - }, - { - name: 'remove', - description: `Removes ${featureName} resource from your local backend which would be removed from the cloud on the next push command`, - }, - ]; - - context.amplify.showHelp(header, commands); - - context.print.info(''); + if (context.parameters.options.help) { + return runHelp(context); + } }, }; diff --git a/packages/amplify-category-interactions/src/commands/interactions/help.ts b/packages/amplify-category-interactions/src/commands/interactions/help.ts new file mode 100644 index 00000000000..adb7dc15830 --- /dev/null +++ b/packages/amplify-category-interactions/src/commands/interactions/help.ts @@ -0,0 +1,5 @@ +import { $TSContext, runHelp, commandsInfo } from 'amplify-cli-core'; + +export const run = (context: $TSContext) => { + runHelp(context, commandsInfo); +}; diff --git a/packages/amplify-category-notifications/src/commands/notifications.ts b/packages/amplify-category-notifications/src/commands/notifications.ts index 720541adaf9..12320d03464 100644 --- a/packages/amplify-category-notifications/src/commands/notifications.ts +++ b/packages/amplify-category-notifications/src/commands/notifications.ts @@ -1,5 +1,6 @@ import { $TSAny, $TSContext } from 'amplify-cli-core'; import { printer } from 'amplify-prompts'; +import { run as runHelp } from './notifications/help'; export const name = 'notifications'; export const alias = ['notification']; @@ -10,6 +11,9 @@ export const alias = ['notification']; * @returns Returns Notifications feature flow output in test/windows environment , and undefined otherwise */ export const run = async (context: $TSContext) : Promise<$TSAny|undefined> => { + if (context.parameters.options.help) { + return runHelp(context); + } if (/^win/.test(process.platform)) { try { const notificationsFlow = await import(`./${name}/${context.parameters.first}`); @@ -18,33 +22,5 @@ export const run = async (context: $TSContext) : Promise<$TSAny|undefined> => { printer.error('Command not found'); } } - - const header = `amplify ${name} `; - const commands = [ - { - name: 'add', - description: 'Adds a notification channel', - }, - { - name: 'remove', - description: 'Removes a notification channel', - }, - { - name: 'update', - description: 'Updates the configuration of a notification channel', - }, - { - name: 'status', - description: 'Lists the enabled/disabled statuses of the available notification channels', - }, - { - name: 'console', - description: 'Opens the Amazon Pinpoint console displaying the current channel settings', - }, - ]; - - context.amplify.showHelp(header, commands); - printer.blankLine(); - return undefined; }; diff --git a/packages/amplify-category-notifications/src/commands/notifications/help.ts b/packages/amplify-category-notifications/src/commands/notifications/help.ts new file mode 100644 index 00000000000..adb7dc15830 --- /dev/null +++ b/packages/amplify-category-notifications/src/commands/notifications/help.ts @@ -0,0 +1,5 @@ +import { $TSContext, runHelp, commandsInfo } from 'amplify-cli-core'; + +export const run = (context: $TSContext) => { + runHelp(context, commandsInfo); +}; diff --git a/packages/amplify-category-predictions/src/commands/predictions.js b/packages/amplify-category-predictions/src/commands/predictions.js index 87b1dfa6681..773f6dcac56 100644 --- a/packages/amplify-category-predictions/src/commands/predictions.js +++ b/packages/amplify-category-predictions/src/commands/predictions.js @@ -1,31 +1,13 @@ +import { run as runHelp } from './predictions/help'; + const categoryName = 'predictions'; module.exports = { name: categoryName, alias: ['Predictions'], run: async context => { - const header = `amplify ${categoryName} `; - - const commands = [ - { - name: 'add', - description: `Takes you through a CLI flow to add a ${categoryName} resource to your local backend`, - }, - { - name: 'remove', - description: `Removes ${categoryName} resource from your local backend which would be removed from the cloud on the next push command`, - }, - { - name: 'update', - description: `Takes you through steps in the CLI to update an ${categoryName} resource`, - }, - { - name: 'console', - description: `Opens a web console to view your ${categoryName} resource`, - }, - ]; - - context.amplify.showHelp(header, commands); - context.print.info(''); + if (context.parameters.options.help) { + return runHelp(context); + } }, }; diff --git a/packages/amplify-category-predictions/src/commands/predictions/help.ts b/packages/amplify-category-predictions/src/commands/predictions/help.ts new file mode 100644 index 00000000000..adb7dc15830 --- /dev/null +++ b/packages/amplify-category-predictions/src/commands/predictions/help.ts @@ -0,0 +1,5 @@ +import { $TSContext, runHelp, commandsInfo } from 'amplify-cli-core'; + +export const run = (context: $TSContext) => { + runHelp(context, commandsInfo); +}; diff --git a/packages/amplify-category-storage/src/commands/storage.ts b/packages/amplify-category-storage/src/commands/storage.ts index 1442b4db2f0..c305d82a844 100644 --- a/packages/amplify-category-storage/src/commands/storage.ts +++ b/packages/amplify-category-storage/src/commands/storage.ts @@ -3,8 +3,12 @@ import { printer } from 'amplify-prompts'; import * as path from 'path'; import { categoryName } from '../constants'; export { categoryName as name } from '../constants'; +import { run as runHelp } from './storage/help'; export async function run(context: $TSContext) { + if (context.parameters.options.help) { + return runHelp(context); + } if (/^win/.test(process.platform)) { try { const { run } = await import(path.join('.', categoryName, context.parameters.first)); @@ -14,38 +18,4 @@ export async function run(context: $TSContext) { printer.error('Command not found'); } } - - const header = `amplify ${categoryName} `; - - const commands = [ - { - name: 'add', - description: `Takes you through steps in the CLI to add a ${categoryName} resource to your local backend`, - }, - { - name: 'import', - description: `Takes you through a CLI flow to import an existing ${categoryName} resource to your local backend`, - }, - { - name: 'update', - description: `Takes you through steps in the CLI to update an ${categoryName} resource`, - }, - { - name: 'push', - description: `Provisions only ${categoryName} cloud resources with the latest local developments`, - }, - { - name: 'remove', - description: `Removes ${categoryName} resource from your local backend. The resource is removed from the cloud on the next push command.`, - }, - { - name: 'override', - description: `Generates 'overrides.ts' for ${categoryName} resource in your local backend. The resource properties can be overridden by editing this file. The resource is overridden in the cloud on the next push command. `, - }, - ]; - - context.amplify.showHelp(header, commands); - - printer.info(''); - return undefined; } diff --git a/packages/amplify-category-storage/src/commands/storage/help.ts b/packages/amplify-category-storage/src/commands/storage/help.ts new file mode 100644 index 00000000000..adb7dc15830 --- /dev/null +++ b/packages/amplify-category-storage/src/commands/storage/help.ts @@ -0,0 +1,5 @@ +import { $TSContext, runHelp, commandsInfo } from 'amplify-cli-core'; + +export const run = (context: $TSContext) => { + runHelp(context, commandsInfo); +}; diff --git a/packages/amplify-category-xr/commands/xr.js b/packages/amplify-category-xr/commands/xr.js index c9e5fb01a1c..1b1762e61c0 100644 --- a/packages/amplify-category-xr/commands/xr.js +++ b/packages/amplify-category-xr/commands/xr.js @@ -4,6 +4,8 @@ module.exports = { name: featureName, alias: ['XR'], run: async context => { + context.print.warning(`The ${featureName} package is deprecated and will be removed in a future version.`); + const header = `amplify ${featureName} `; const commands = [ diff --git a/packages/amplify-cli-core/API.md b/packages/amplify-cli-core/API.md index 135ec2e8047..aeee33d6d84 100644 --- a/packages/amplify-cli-core/API.md +++ b/packages/amplify-cli-core/API.md @@ -408,6 +408,25 @@ export class CloudformationProviderFacade { static prePushCfnTemplateModifier(context: $TSContext, template: Template_2): Promise<(template: Template_2) => Promise>; } +// @public (undocumented) +export type CommandFlagInfo = { + short: string; + long: string; + flagDescription: string; +}; + +// @public (undocumented) +export type CommandInfo = { + command: string; + commandDescription: string; + commandUsage: string; + commandFlags: Array; + subCommands: Array; +}; + +// @public (undocumented) +export const commandsInfo: Array; + // @public (undocumented) export class ConfigurationError extends Error { } @@ -982,6 +1001,12 @@ export class JSONUtilities { } | undefined) => void; } +// @public (undocumented) +export function lookUpCommand(commandsInfo: Array, commandName: string): CommandInfo | undefined; + +// @public (undocumented) +export function lookUpSubcommand(commandsInfo: Array, commandName: string, subcommandName: string): SubCommandInfo | undefined; + // Warning: (ae-forgotten-export) The symbol "deploymentSecretMerge" needs to be exported by the entry point index.d.ts // // @public (undocumented) @@ -1048,6 +1073,12 @@ export type PackageManager = { // @public (undocumented) export type PackageManagerType = 'yarn' | 'npm'; +// @public (undocumented) +export function parseHelpCommands(input: $TSAny, commandsInfo: Array): { + command: string; + subCommand: string; +}; + // @public (undocumented) export type PartialAmplifyExceptionOptions = Partial & { message: string; @@ -1285,6 +1316,9 @@ export interface ResourceTuple { resourceName: string; } +// @public (undocumented) +export function runHelp(context: $TSContext, commandsInfo: Array): void; + // @public (undocumented) export const SecretFileMode = 384; @@ -1417,6 +1451,14 @@ export const stateManager: StateManager; // @public (undocumented) export type StepStatusParameters = Omit; +// @public (undocumented) +export type SubCommandInfo = { + subCommand: string; + subCommandDescription: string; + subCommandUsage: string; + subCommandFlags: Array; +}; + // @public (undocumented) export const supportedEnvEvents: HooksVerb[]; diff --git a/packages/amplify-cli-core/src/__tests__/help.test.ts b/packages/amplify-cli-core/src/__tests__/help.test.ts new file mode 100644 index 00000000000..bace911ccc0 --- /dev/null +++ b/packages/amplify-cli-core/src/__tests__/help.test.ts @@ -0,0 +1,261 @@ +import { $TSContext, CommandInfo, runHelp, commandsInfo, lookUpCommand, lookUpSubcommand, parseHelpCommands } from 'amplify-cli-core'; +import { printer } from 'amplify-prompts'; + +describe('amplify help functions: ', () => { + printer.info = jest.fn(); + const mockCommandsInfo: Array = [ + { + command: 'init', + commandDescription: 'Initializes a new project, sets up deployment resources in the cloud, and makes your project ready for Amplify', + commandUsage: 'amplify init [flags]', + commandFlags: [ + { + short: 'y', + long: 'yes', + flagDescription: 'skip all interactive prompts by selecting default options', + }, + { + short: '', + long: 'amplify', + flagDescription: 'basic information of the project', + }, + { + short: '', + long: 'frontend', + flagDescription: "information for the project's frontend appliction", + }, + { + short: '', + long: 'providers', + flagDescription: 'configuration settings for provider plugins', + }, + { + short: '', + long: 'categories', + flagDescription: 'configuration settings for resources in the given categories', + }, + { + short: '', + long: 'app', + flagDescription: 'Specify a GitHub repository from which to create an Amplify project', + }, + { + short: '', + long: 'permissions-boundary ', + flagDescription: 'Specify an IAM permissions boundary for the roles created during init', + }, + ], + subCommands: [], + }, + { + command: 'configure', + commandDescription: 'Configure the Amplify CLI for usage', + commandUsage: 'amplify configure ', + commandFlags: [], + subCommands: [ + { + subCommand: 'project', + subCommandDescription: 'Configure the attributes of your project such as switching front-end framework', + subCommandUsage: 'amplify configure project [flags]', + subCommandFlags: [ + { + short: 'y', + long: 'yes', + flagDescription: 'skip all interactive prompts by selecting default options', + }, + { + short: '', + long: 'amplify', + flagDescription: 'basic information of the project', + }, + { + short: '', + long: 'frontend', + flagDescription: "information for the project's frontend appliction", + }, + { + short: '', + long: 'providers', + flagDescription: 'configuration settings for provider plugins', + }, + ], + }, + { + subCommand: 'hosting', + subCommandDescription: 'Configure hosting resources including S3, CloudFront, and publish ignore', + subCommandUsage: 'amplify hosting project', + subCommandFlags: [], + }, + { + subCommand: 'codegen', + subCommandDescription: 'Configure GraphQL codegen', + subCommandUsage: 'amplify configure codegen', + subCommandFlags: [], + }, + ], + }, + { + command: 'mock', + commandDescription: 'Run mock server for testing categories locally', + commandUsage: 'amplify mock ', + commandFlags: [], + subCommands: [ + { + subCommand: 'api', + subCommandDescription: 'Run mock server for testing API locally', + subCommandUsage: 'amplify mock api', + subCommandFlags: [], + }, + { + subCommand: 'storage', + subCommandDescription: 'Run mock server for testing storage locally', + subCommandUsage: 'amplify mock storage', + subCommandFlags: [], + }, + { + subCommand: 'function', + subCommandDescription: 'Run mock server for testing functions locally', + subCommandUsage: 'amplify mock function [flags]', + subCommandFlags: [ + { + short: '', + long: 'event ', + flagDescription: 'specified JSON file as the event to pass to the Lambda handler', + }, + { + short: '', + long: 'timeout ', + flagDescription: 'Override the default 10 second function response timeout with a custom timeout value', + }, + ], + }, + { + subCommand: 'function ', + subCommandDescription: 'Run mock server for testing a specific function locally', + subCommandUsage: 'amplify mock function ', + subCommandFlags: [], + }, + ], + }, + ]; + + it('lookup valid command (init) and expect not null', () => { + let initCommandInfo = lookUpCommand(mockCommandsInfo, 'init'); + expect(initCommandInfo).not.toBeUndefined(); + }); + + it('lookup invalid command and expect null', () => { + let invalidCommandInfo = lookUpCommand(mockCommandsInfo, 'invalidcommand'); + expect(invalidCommandInfo).toBeUndefined(); + }); + + it('lookup valid command (init) and expect correct command name', () => { + let initCommandInfo = lookUpCommand(mockCommandsInfo, 'init'); + expect(initCommandInfo!.command).toBe('init'); + }); + + it('lookup valid command (configure) and expect correct command name', () => { + let initCommandInfo = lookUpCommand(mockCommandsInfo, 'configure'); + expect(initCommandInfo!.command).toBe('configure'); + }); + + it('lookup valid subcommand (configure project) and expect not null', () => { + let configureProjectSubCommandInfo = lookUpSubcommand(mockCommandsInfo, 'configure', 'project'); + expect(configureProjectSubCommandInfo).not.toBeUndefined(); + }); + + it('lookup invalid subcommand and expect null', () => { + let invalidSubCommandInfo = lookUpSubcommand(mockCommandsInfo, 'invalidcommand', 'invalidsubcommand'); + expect(invalidSubCommandInfo).toBeUndefined(); + }); + + it('lookup valid subcommand (configure project) and expect correct subcommand name', () => { + let configureProjectSubCommandInfo = lookUpSubcommand(mockCommandsInfo, 'configure', 'project'); + expect(configureProjectSubCommandInfo!.subCommand).toBe('project'); + }); + + it('lookup valid subcommand (configure hosting) correct subcommand name', () => { + let configureHostingSubCommandInfo = lookUpSubcommand(mockCommandsInfo, 'configure', 'hosting'); + expect(configureHostingSubCommandInfo!.subCommand).toBe('hosting'); + }); + + it('parse command (configure) from input', () => { + const configureInput = { + argv: ['node', 'amplify', 'configure', '-h'], + command: 'help', + options: { help: true }, + plugin: 'core', + subCommands: ['configure'], + }; + let specifiedCommands = parseHelpCommands(configureInput, mockCommandsInfo); + expect(specifiedCommands.command).toBe('configure'); + expect(specifiedCommands.subCommand).toBe(''); + }); + + it('parse command and subcommand (mock function) from input', () => { + const mockFunctionInput = { + argv: ['node', 'amplify', 'mock', 'function', '-h'], + plugin: 'mock', + command: 'help', + options: { help: true, yes: false }, + subCommands: ['mock', 'function'], + }; + let specifiedCommands = parseHelpCommands(mockFunctionInput, mockCommandsInfo); + expect(specifiedCommands.command).toBe('mock'); + expect(specifiedCommands.subCommand).toBe('function'); + }); + + it('run help invalid command', () => { + let mockContext: $TSContext; + mockContext = ({ + input: { + argv: ['node', 'amplify', 'invalid', 'command', '-h'], + command: 'help', + subCommands: ['invalid', 'command'], + options: { help: true, yes: false }, + plugin: 'core', + }, + } as unknown) as $TSContext; + + runHelp(mockContext, commandsInfo); + expect(printer.info).toBeCalledWith(' ' + 'amplify [flags]'); + }); + + it('run help command (mock)', () => { + let mockContext: $TSContext; + + mockContext = ({ + print: { + info: jest.fn(), + }, + input: { + argv: ['node', 'amplify', 'mock', '-h'], + command: 'help', + subCommands: ['mock'], + options: { help: true, yes: false }, + plugin: 'mock', + }, + } as unknown) as $TSContext; + runHelp(mockContext, commandsInfo); + expect(printer.info).toBeCalledWith(' ' + 'amplify mock '); + }); + + it('run help subcommand (mock function)', () => { + let mockContext: $TSContext; + + mockContext = ({ + print: { + info: jest.fn(), + }, + input: { + argv: ['node', 'amplify', 'mock', 'function', '-h'], + command: 'help', + subCommands: ['mock', 'function'], + options: { help: true, yes: false }, + plugin: 'mock', + }, + } as unknown) as $TSContext; + runHelp(mockContext, commandsInfo); + expect(printer.info).toBeCalledWith(' ' + 'amplify mock function [flags]'); + }); +}); diff --git a/packages/amplify-cli-core/src/help/commands-info.ts b/packages/amplify-cli-core/src/help/commands-info.ts new file mode 100644 index 00000000000..ddafad6bc06 --- /dev/null +++ b/packages/amplify-cli-core/src/help/commands-info.ts @@ -0,0 +1,1127 @@ +import { CommandInfo } from './help-helpers'; + +export const commandsInfo: Array = [ + { + command: 'init', + commandDescription: 'Initialize a new Amplify project', + commandUsage: 'amplify init [flags]', + commandFlags: [ + { + short: 'y', + long: 'yes', + flagDescription: 'Skip all interactive prompts by selecting default options', + }, + { + short: '', + long: 'amplify', + flagDescription: 'Basic information of the project', + }, + { + short: '', + long: 'frontend', + flagDescription: "Information for the project's frontend appliction", + }, + { + short: '', + long: 'providers', + flagDescription: 'Configuration settings for provider plugins', + }, + { + short: '', + long: 'categories', + flagDescription: 'Configuration settings for resources in the given categories', + }, + { + short: '', + long: 'app', + flagDescription: 'Specify a GitHub repository from which to create an Amplify project', + }, + { + short: '', + long: 'permissions-boundary ', + flagDescription: 'Specify an IAM permissions boundary for the roles created during init', + }, + ], + subCommands: [], + }, + { + command: 'configure', + commandDescription: 'Configure the CLI to work with your AWS profile', + commandUsage: 'amplify configure ', + commandFlags: [], + subCommands: [ + { + subCommand: 'project', + subCommandDescription: 'Configure the attributes of your project', + subCommandUsage: 'amplify configure project [flags]', + subCommandFlags: [ + { + short: 'y', + long: 'yes', + flagDescription: 'Skip all interactive prompts by selecting default options', + }, + { + short: '', + long: 'amplify', + flagDescription: 'Basic information of the project', + }, + { + short: '', + long: 'frontend', + flagDescription: "Information for the project's frontend appliction", + }, + { + short: '', + long: 'providers', + flagDescription: 'Configuration settings for provider plugins', + }, + ], + }, + { + subCommand: 'hosting', + subCommandDescription: 'Configure hosting resources for your Amplify project', + subCommandUsage: 'amplify configure hosting', + subCommandFlags: [], + }, + { + subCommand: 'codegen', + subCommandDescription: 'Configure GraphQL codegen for your Amplify project', + subCommandUsage: 'amplify configure codegen', + subCommandFlags: [], + }, + ], + }, + { + command: 'push', + commandDescription: 'Provisions cloud resources with the latest local changes', + commandUsage: 'amplify push [flags]', + commandFlags: [ + { + short: '', + long: 'codegen', + flagDescription: 'Configuration for GraphQL codegen', + }, + { + short: 'f', + long: 'force', + flagDescription: 'Pushes all resources regardless of update status and bypasses all guardrails', + }, + { + short: 'y', + long: 'yes', + flagDescription: 'Skip all interactive prompts by selecting default options', + }, + { + short: '', + long: 'allow-destructive-graphql-schema-updates', + flagDescription: 'Pushes schema changes that require removal or replacement of underlying tables', + }, + { + short: '', + long: 'headless', + flagDescription: 'Headless JSON payload', + }, + ], + subCommands: [ + { + subCommand: '', + subCommandDescription: 'Provisions cloud resources with the latest local changes for a single category', + subCommandUsage: 'amplify push [flags]', + subCommandFlags: [], + }, + ], + }, + { + command: 'pull', + commandDescription: 'Fetch upstream backend changes from the cloud and updates the local environment', + commandUsage: 'amplify pull [flags]', + commandFlags: [ + { + short: 'y', + long: 'yes', + flagDescription: 'Skip all interactive prompts by selecting default options', + }, + { + short: '', + long: 'restore', + flagDescription: 'Overwrite your local backend changes with configurations from the cloud', + }, + { + short: '', + long: 'amplify', + flagDescription: 'Basic information of the project', + }, + { + short: '', + long: 'frontend', + flagDescription: "Information for the project's frontend appliction", + }, + { + short: '', + long: 'providers', + flagDescription: 'Configuration settings for provider plugins', + }, + ], + subCommands: [], + }, + { + command: 'env', + commandDescription: 'Displays and manages environment related information for your Amplify project', + commandUsage: 'amplify env ', + commandFlags: [], + subCommands: [ + { + subCommand: 'add', + subCommandDescription: 'Adds a new environment to your Amplify Project', + subCommandUsage: 'amplify env add', + subCommandFlags: [], + }, + { + subCommand: 'checkout ', + subCommandDescription: 'Moves your environment to the environment specified in the command', + subCommandUsage: 'amplify env checkout [flags]', + subCommandFlags: [ + { + short: '', + long: 'restore', + flagDescription: 'Overwrite your local backend with that of the specified environment', + }, + ], + }, + { + subCommand: 'get', + subCommandDescription: 'Displays and manages environment related information for your Amplify project', + subCommandUsage: 'amplify env get [flags]', + subCommandFlags: [ + { + short: '', + long: 'name ', + flagDescription: 'Specify name', + }, + { + short: '', + long: 'json', + flagDescription: 'Get environment information in JSON format', + }, + ], + }, + { + subCommand: 'import', + subCommandDescription: 'Imports an existing Amplify project environment stack to your local backend', + subCommandUsage: 'amplify env import [flags]', + subCommandFlags: [ + { + short: '', + long: 'name ', + flagDescription: 'Specify name', + }, + { + short: '', + long: 'config ', + flagDescription: 'Specify configuration file', + }, + { + short: '', + long: 'awsInfo ', + flagDescription: 'Specify AWS account info', + }, + ], + }, + { + subCommand: 'list', + subCommandDescription: 'Displays a list of all the environments in your Amplify project', + subCommandUsage: 'amplify env list [flags]', + subCommandFlags: [ + { + short: '', + long: 'details', + flagDescription: 'List environment details', + }, + { + short: '', + long: 'json', + flagDescription: 'List environment details in JSON format', + }, + ], + }, + { + subCommand: 'pull', + subCommandDescription: 'Pulls your environment with the current cloud environment', + subCommandUsage: 'amplify env pull [flags]', + subCommandFlags: [ + { + short: 'y', + long: 'yes', + flagDescription: 'Skip all interactive prompts by selecting default options', + }, + { + short: '', + long: 'restore', + flagDescription: 'Overwrite your local backend with that of the specified environment', + }, + ], + }, + { + subCommand: 'remove', + subCommandDescription: 'Removes an environment from the Amplify project', + subCommandUsage: 'amplify env remove', + subCommandFlags: [], + }, + { + subCommand: 'update', + subCommandDescription: 'Update the environment configuration', + subCommandUsage: 'amplify env update [flags]', + subCommandFlags: [ + { + short: '', + long: 'permissions-boundary ', + flagDescription: 'Set a permisions boundary', + }, + ], + }, + ], + }, + { + command: 'add', + commandDescription: 'Adds a resource for an Amplify category in your local backend', + commandUsage: 'amplify add ', + commandFlags: [], + subCommands: [ + { + subCommand: '', + subCommandDescription: 'Adds a resource for an Amplify category in your local backend', + subCommandUsage: 'amplify add [flags]', + subCommandFlags: [ + { + short: '', + long: 'headless', + flagDescription: 'Headless JSON payload', + }, + ], + }, + ], + }, + { + command: 'status', + commandDescription: 'Shows the state of local resources not yet pushed to the cloud', + commandUsage: 'amplify status [flags]', + commandFlags: [ + { + short: 'v', + long: 'verbose', + flagDescription: 'Shows verbose details, including cloudformation differences', + }, + ], + subCommands: [ + { + subCommand: '', + subCommandDescription: 'Shows the state of local resources not yet pushed to the cloud', + subCommandUsage: 'amplify status [flags]', + subCommandFlags: [], + }, + ], + }, + { + command: 'plugin', + commandDescription: 'Configure Amplify plugins', + commandUsage: 'amplify pluigin ', + commandFlags: [], + subCommands: [ + { + subCommand: 'init', + subCommandDescription: 'Scaffolds a skeleton Amplify CLI plugin', + subCommandUsage: 'amplify plugin init', + subCommandFlags: [], + }, + { + subCommand: 'configure', + subCommandDescription: 'Configures Amplify CLI plugin options', + subCommandUsage: 'amplify plugin configure', + subCommandFlags: [], + }, + { + subCommand: 'list', + subCommandDescription: 'Lists general plugin information', + subCommandUsage: 'amplify plugin list', + subCommandFlags: [], + }, + { + subCommand: 'scan', + subCommandDescription: 'Explicitly starts a scan/search for new and existing plugins', + subCommandUsage: 'amplify plugin scan', + subCommandFlags: [], + }, + { + subCommand: 'add', + subCommandDescription: 'Explicitly adds a plugin for the Amplify CLI to use', + subCommandUsage: 'amplify plugin add', + subCommandFlags: [], + }, + { + subCommand: 'remove', + subCommandDescription: 'Explicitly removes a plugin from the Amplify CLI', + subCommandUsage: 'amplify plugin remove', + subCommandFlags: [], + }, + { + subCommand: 'verify', + subCommandDescription: 'Verifies if a plugin package/directory is a valid Amplify CLI plugin', + subCommandUsage: 'amplify plugin verify', + subCommandFlags: [], + }, + ], + }, + { + command: 'update', + commandDescription: 'Update resource for an Amplify category in your local backend', + commandUsage: 'amplify update ', + commandFlags: [], + subCommands: [ + { + subCommand: '', + subCommandDescription: 'Update resource for an Amplify category in your local backend', + subCommandUsage: 'amplify update [flags]', + subCommandFlags: [ + { + short: '', + long: 'headless', + flagDescription: 'Headless JSON payload', + }, + ], + }, + ], + }, + { + command: 'publish', + commandDescription: 'Executes amplify push and hosts the frontend app', + commandUsage: 'amplify publish [flags]', + commandFlags: [ + { + short: 'y', + long: 'yes', + flagDescription: 'Automatically accept publish prompt', + }, + { + short: '', + long: 'codegen', + flagDescription: 'Configuration for GraphQL codegen', + }, + { + short: 'f', + long: 'force', + flagDescription: 'Pushes all resources regardless of update status and bypasses all guardrails', + }, + { + short: '', + long: 'allow-destructive-graphql-schema-updates', + flagDescription: 'Pushes schema changes that require removal or replacement of underlying tables', + }, + { + short: 'c', + long: 'invalidateCloudFront', + flagDescription: 'Send an invalidation request to the Amazon CloudFront service', + }, + ], + subCommands: [], + }, + { + command: 'remove', + commandDescription: 'Removes a resource for an Amplify category in your local backend', + commandUsage: 'amplify remove ', + commandFlags: [], + subCommands: [ + { + subCommand: '', + subCommandDescription: 'Removes a resource for an Amplify category in your local backend', + subCommandUsage: 'amplify remove [flags]', + subCommandFlags: [ + { + short: '', + long: 'headless', + flagDescription: 'Headless JSON payload', + }, + ], + }, + ], + }, + { + command: 'console', + commandDescription: 'Opens the web console for the selected cloud resource', + commandUsage: 'amplify console ', + commandFlags: [], + subCommands: [], + }, + { + command: 'delete', + commandDescription: 'Delete the Amplify project', + commandUsage: 'amplify delete [flags]', + commandFlags: [ + { + short: 'y', + long: 'yes', + flagDescription: 'Skip all interactive prompts by selecting default options', + }, + { + short: 'f', + long: 'force', + flagDescription: 'Skip all interactive prompts by selecting default options', + }, + ], + subCommands: [], + }, + { + command: 'upgrade', + commandDescription: 'Download and install the latest version of the Amplify CLI', + commandUsage: 'amplify upgrade', + commandFlags: [], + subCommands: [], + }, + { + command: 'import', + commandDescription: 'Imports existing resources to your local backend', + commandUsage: 'amplify import [flags]', + commandFlags: [ + { + short: '', + long: 'headless', + flagDescription: 'Headless JSON payload', + }, + ], + subCommands: [ + { + subCommand: 'auth', + subCommandDescription: 'Imports an existing auth resource to your local backend', + subCommandUsage: 'amplify import auth [flags]', + subCommandFlags: [], + }, + { + subCommand: 'env', + subCommandDescription: 'Imports an existing Amplify project environment stack to your local backend', + subCommandUsage: 'amplify import env [flags]', + subCommandFlags: [], + }, + { + subCommand: 'storage', + subCommandDescription: 'Imports an existing storage resource to your local backend', + subCommandUsage: 'amplify import storage [flags]', + subCommandFlags: [], + }, + ], + }, + { + command: 'override', + commandDescription: 'Override Amplify-generated resources with Cloud Development Kit (CDK)', + commandUsage: 'amplify override ', + commandFlags: [], + subCommands: [ + { + subCommand: 'api', + subCommandDescription: 'Override Amplify-generated GraphQL API resources', + subCommandUsage: 'amplify override api', + subCommandFlags: [], + }, + { + subCommand: 'auth', + subCommandDescription: 'Override Amplify-generated auth resources', + subCommandUsage: 'amplify override auth', + subCommandFlags: [], + }, + { + subCommand: 'storage', + subCommandDescription: 'Override Amplify-generated storage resources', + subCommandUsage: 'amplify override storage', + subCommandFlags: [], + }, + { + subCommand: 'project', + subCommandDescription: 'override Amplify-generated project-level resources, such as IAM roles', + subCommandUsage: 'amplify override project', + subCommandFlags: [], + }, + ], + }, + { + command: 'diagnose', + commandDescription: 'Capture non-sensitive Amplify backend metadata for debugging purposes', + commandUsage: 'amplify diagnose [flags]', + commandFlags: [ + { + short: '', + long: 'send-report', + flagDescription: 'Share non-sensitive configurations of your Amplify backend with the Amplify team', + }, + { + short: '', + long: 'auto-send-off', + flagDescription: 'Opt out of sharing your project configurations with Amplify on failures', + }, + { + short: '', + long: 'auto-send-on', + flagDescription: 'Opt in to sharing your project configurations with Amplify on failures', + }, + ], + subCommands: [], + }, + { + command: 'logout', + commandDescription: 'Logs out of Amplify Studio', + commandUsage: 'amplify logout [flags]', + commandFlags: [ + { + short: '', + long: 'appId ', + flagDescription: 'Specify app ID', + }, + ], + subCommands: [], + }, + { + command: 'export', + commandDescription: 'Export Amplify CLI-generated backends as a Cloud Development Kit (CDK) stack', + commandUsage: 'amplify export [flags]', + commandFlags: [ + { + short: '', + long: 'out ', + flagDescription: 'Specify the output path, where this is typically the path to your CDK project', + }, + ], + subCommands: [], + }, + { + command: 'uninstall', + commandDescription: 'Uninstall the Amplify CLI', + commandUsage: 'amplify uninstall', + commandFlags: [], + subCommands: [], + }, + { + command: 'serve', + commandDescription: 'Executes amplify push, and then test run the client-side application locally', + commandUsage: 'amplify serve', + commandFlags: [], + subCommands: [], + }, + { + command: 'mock', + commandDescription: 'Run mock server for testing categories locally', + commandUsage: 'amplify mock ', + commandFlags: [], + subCommands: [ + { + subCommand: 'api', + subCommandDescription: 'Run mock server for testing API locally', + subCommandUsage: 'amplify mock api', + subCommandFlags: [], + }, + { + subCommand: 'storage', + subCommandDescription: 'Run mock server for testing storage locally', + subCommandUsage: 'amplify mock storage', + subCommandFlags: [], + }, + { + subCommand: 'function', + subCommandDescription: 'Run mock server for testing functions locally', + subCommandUsage: 'amplify mock function [flags]', + subCommandFlags: [ + { + short: '', + long: 'event ', + flagDescription: 'Specified JSON file as the event to pass to the Lambda handler', + }, + { + short: '', + long: 'timeout ', + flagDescription: 'Override the default 10-second function response timeout', + }, + ], + }, + { + subCommand: 'function ', + subCommandDescription: 'Run mock server for testing a specific function locally', + subCommandUsage: 'amplify mock function ', + subCommandFlags: [], + }, + ], + }, + { + command: 'codegen', + commandDescription: 'Generates GraphQL statements and type annotations', + commandUsage: 'amplify codegen ', + commandFlags: [], + subCommands: [ + { + subCommand: 'configure', + subCommandDescription: 'Configure GraphQL codegen for your Amplify project', + subCommandUsage: 'amplify codegen configure', + subCommandFlags: [], + }, + { + subCommand: 'statements', + subCommandDescription: 'Generates GraphQL statements (queries, mutations, and subscriptions)', + subCommandUsage: 'amplify codegen statements', + subCommandFlags: [], + }, + { + subCommand: 'types', + subCommandDescription: 'Generates GraphQL type annotations', + subCommandUsage: 'amplify codegen types', + subCommandFlags: [], + }, + { + subCommand: 'models', + subCommandDescription: 'Generates GraphQL DataStore models', + subCommandUsage: 'amplify codegen models', + subCommandFlags: [], + }, + ], + }, + { + command: 'api', + commandDescription: 'Enable an easy and secure solution to access backend data', + commandUsage: 'amplify api ', + commandFlags: [], + subCommands: [ + { + subCommand: 'add-graphql-datasource', + subCommandDescription: 'Add an RDS datasource to your GraphQL API', + subCommandUsage: 'amplify api add-graphql-datasource', + subCommandFlags: [], + }, + { + subCommand: 'rebuild', + subCommandDescription: 'Removes all GraphQL resources and recreates the API (only use in dev envs)', + subCommandUsage: 'amplify api rebuild', + subCommandFlags: [], + }, + { + subCommand: 'add', + subCommandDescription: 'Takes you through a CLI flow to add a api resource to your local backend', + subCommandUsage: 'amplify api add', + subCommandFlags: [], + }, + { + subCommand: 'push', + subCommandDescription: 'Provisions API cloud resources with the latest local developments', + subCommandUsage: 'amplify api push', + subCommandFlags: [], + }, + { + subCommand: 'remove', + subCommandDescription: 'Removes API resource from your local backend', + subCommandUsage: 'amplify api remove', + subCommandFlags: [], + }, + { + subCommand: 'update', + subCommandDescription: 'Updates an API resource', + subCommandUsage: 'amplify api update', + subCommandFlags: [], + }, + { + subCommand: 'gql-compile', + subCommandDescription: 'Compiles your GraphQL schema and generates a CloudFormation template', + subCommandUsage: 'amplify api gql-conpile', + subCommandFlags: [], + }, + { + subCommand: 'console', + subCommandDescription: 'Opens the web console for the selected api service', + subCommandUsage: 'amplify api console', + subCommandFlags: [], + }, + { + subCommand: 'migrate', + subCommandDescription: 'Migrates GraphQL schemas to the latest GraphQL transformer version', + subCommandUsage: 'amplify api migrate', + subCommandFlags: [], + }, + { + subCommand: 'override', + subCommandDescription: 'Generates overrides file to apply custom modifications to CloudFormation', + subCommandUsage: 'amplify api override', + subCommandFlags: [], + }, + { + subCommand: 'push', + subCommandDescription: 'Provisions cloud resources with the latest local changes', + subCommandUsage: 'amplify api push', + subCommandFlags: [], + }, + ], + }, + { + command: 'storage', + commandDescription: 'Enable a mechanism for managing user content', + commandUsage: 'amplify storage ', + commandFlags: [], + subCommands: [ + { + subCommand: 'add', + subCommandDescription: 'Adds a storage resource to your local backend', + subCommandUsage: 'amplify storage add', + subCommandFlags: [], + }, + { + subCommand: 'import', + subCommandDescription: 'Import an existing storage resource to your local backend', + subCommandUsage: 'amplify storage import', + subCommandFlags: [], + }, + { + subCommand: 'update', + subCommandDescription: 'Update a storage resource', + subCommandUsage: 'amplify storage update', + subCommandFlags: [], + }, + { + subCommand: 'push', + subCommandDescription: 'Provisions storage cloud resources with the latest local developments', + subCommandUsage: 'amplify storage push', + subCommandFlags: [], + }, + { + subCommand: 'remove', + subCommandDescription: 'Removes storage resource from your local backend', + subCommandUsage: 'amplify storage remove', + subCommandFlags: [], + }, + { + subCommand: 'override', + subCommandDescription: "Generates 'overrides.ts' for overriding storage resources", + subCommandUsage: 'amplify storage override', + subCommandFlags: [], + }, + { + subCommand: 'push', + subCommandDescription: 'Provisions cloud resources with the latest local changes', + subCommandUsage: 'amplify storage override', + subCommandFlags: [], + }, + ], + }, + { + command: 'notifications', + commandDescription: 'Configure notifications for your Amplify project', + commandUsage: 'amplify notifications', + commandFlags: [], + subCommands: [ + { + subCommand: 'add', + subCommandDescription: 'Adds a notification channel', + subCommandUsage: 'amplify notifications add', + subCommandFlags: [], + }, + { + subCommand: 'remove', + subCommandDescription: 'Removes a notification channel', + subCommandUsage: 'amplify notifications remove', + subCommandFlags: [], + }, + { + subCommand: 'update', + subCommandDescription: 'Updates the configuration of a notification channel', + subCommandUsage: 'amplify notifications update', + subCommandFlags: [], + }, + { + subCommand: 'status', + subCommandDescription: 'Lists the enabled/disabled statuses of the available notification channels', + subCommandUsage: 'amplify notifications status', + subCommandFlags: [], + }, + { + subCommand: 'console', + subCommandDescription: 'Opens the Amazon Pinpoint console displaying the current channel settings', + subCommandUsage: 'amplify notifications console', + subCommandFlags: [], + }, + { + subCommand: 'push', + subCommandDescription: 'Provisions cloud resources with the latest local changes', + subCommandUsage: 'amplify notifications push', + subCommandFlags: [], + }, + ], + }, + { + command: 'auth', + commandDescription: 'Enable sign-in, sign-up, and sign-out for your app', + commandUsage: 'amplify auth ', + commandFlags: [], + subCommands: [ + { + subCommand: 'add', + subCommandDescription: 'Adds an auth resource to your local backend', + subCommandUsage: 'amplify auth add', + subCommandFlags: [], + }, + { + subCommand: 'import', + subCommandDescription: 'Imports an existing auth resource to your local backend', + subCommandUsage: 'amplify auth import', + subCommandFlags: [], + }, + { + subCommand: 'push', + subCommandDescription: 'Provisions only auth cloud resources with the latest local developments', + subCommandUsage: 'amplify auth push', + subCommandFlags: [], + }, + { + subCommand: 'remove', + subCommandDescription: 'Removes auth resources from your local backend', + subCommandUsage: 'amplify auth remove', + subCommandFlags: [], + }, + { + subCommand: 'update', + subCommandDescription: 'Updates the auth resource from your local backend', + subCommandUsage: 'amplify auth update', + subCommandFlags: [], + }, + { + subCommand: 'console', + subCommandDescription: 'Opens the web console for the auth category', + subCommandUsage: 'amplify auth console', + subCommandFlags: [], + }, + { + subCommand: 'push', + subCommandDescription: 'Provisions cloud resources with the latest local changes', + subCommandUsage: 'amplify auth push', + subCommandFlags: [], + }, + ], + }, + { + command: 'geo', + commandDescription: 'Configure geo resources for your Amplify project', + commandUsage: 'amplify geo ', + commandFlags: [], + subCommands: [ + { + subCommand: 'add', + subCommandDescription: 'Takes you through a CLI flow to add a geo resource to your local backend', + subCommandUsage: 'amplify geo add', + subCommandFlags: [], + }, + { + subCommand: 'update', + subCommandDescription: 'Takes you through steps in the CLI to update a geo resource', + subCommandUsage: 'amplify geo update', + subCommandFlags: [], + }, + { + subCommand: 'push', + subCommandDescription: 'Provisions only geo cloud resources with the latest local developments', + subCommandUsage: 'amplify geo push', + subCommandFlags: [], + }, + { + subCommand: 'remove', + subCommandDescription: 'Removes geo resource from your local backend', + subCommandUsage: 'amplify geo remove', + subCommandFlags: [], + }, + { + subCommand: 'console', + subCommandDescription: 'Opens the web console for the geo category', + subCommandUsage: 'amplify geo console', + subCommandFlags: [], + }, + ], + }, + { + command: 'analytics', + commandDescription: 'Add analytics resources to your Amplify project', + commandUsage: 'amplify analytics ', + commandFlags: [], + subCommands: [ + { + subCommand: 'add', + subCommandDescription: 'Takes you through a CLI flow to add an analytics resource to your local backend', + subCommandUsage: 'amplify analytics add', + subCommandFlags: [], + }, + { + subCommand: 'update', + subCommandDescription: 'Takes you through steps in the CLI to update an analytics resource', + subCommandUsage: 'amplify analytics update', + subCommandFlags: [], + }, + { + subCommand: 'push', + subCommandDescription: 'Provisions only analytics cloud resources with the latest local developments', + subCommandUsage: 'amplify analytics push', + subCommandFlags: [], + }, + { + subCommand: 'remove', + subCommandDescription: 'Removes analytics resource from your local backend', + subCommandUsage: 'amplify analytics remove', + subCommandFlags: [], + }, + { + subCommand: 'console', + subCommandDescription: 'Opens the web console for the analytics category', + subCommandUsage: 'amplify analytics console', + subCommandFlags: [], + }, + ], + }, + { + command: 'function', + commandDescription: 'Configure function resources for your Amplify project', + commandUsage: 'amplify function ', + commandFlags: [], + subCommands: [ + { + subCommand: 'add', + subCommandDescription: 'Takes you through a CLI flow to add a function resource to your local backend', + subCommandUsage: 'amplify function add', + subCommandFlags: [], + }, + { + subCommand: 'update', + subCommandDescription: 'Takes you through a CLI flow to update an existing function resource', + subCommandUsage: 'amplify function update', + subCommandFlags: [], + }, + { + subCommand: 'push', + subCommandDescription: 'Provisions only function cloud resources with the latest local developments', + subCommandUsage: 'amplify function push', + subCommandFlags: [], + }, + { + subCommand: 'build', + subCommandDescription: 'Removes function resource from your local backend', + subCommandUsage: 'amplify function build', + subCommandFlags: [], + }, + { + subCommand: 'remove', + subCommandDescription: 'Builds all the functions in the project', + subCommandUsage: 'amplify function remove', + subCommandFlags: [], + }, + { + subCommand: 'console', + subCommandDescription: 'Opens the web console for the function category', + subCommandUsage: 'amplify function console', + subCommandFlags: [], + }, + ], + }, + { + command: 'hosting', + commandDescription: 'Configure hosting resources for your Amplify project', + commandUsage: 'amplify hosting ', + commandFlags: [], + subCommands: [ + { + subCommand: 'serve', + subCommandDescription: 'Opens your deployed site', + subCommandUsage: 'amplify hosting serve', + subCommandFlags: [], + }, + { + subCommand: 'configure', + subCommandDescription: 'Configure hosting resources via the Amplify Console', + subCommandUsage: 'amplify hosting configure', + subCommandFlags: [], + }, + { + subCommand: 'publish', + subCommandDescription: 'Publishes changes to manually deployed apps', + subCommandUsage: 'amplify hosting publish', + subCommandFlags: [], + }, + { + subCommand: 'remove', + subCommandDescription: 'Remove hosting from you app', + subCommandUsage: 'amplify hosting remove', + subCommandFlags: [], + }, + { + subCommand: 'push', + subCommandDescription: 'Provisions cloud resources with the latest local changes', + subCommandUsage: 'amplify hosting push', + subCommandFlags: [], + }, + ], + }, + { + command: 'interactions', + commandDescription: 'Configure interactions resources for your Amplify project', + commandUsage: 'amplify interactions ', + commandFlags: [], + subCommands: [ + { + subCommand: 'add', + subCommandDescription: 'Adds a interactions resources to your local backend', + subCommandUsage: 'amplify interactions add', + subCommandFlags: [], + }, + { + subCommand: 'update', + subCommandDescription: 'Takes you through a CLI flow to update an interactions resource', + subCommandUsage: 'amplify interactions update', + subCommandFlags: [], + }, + { + subCommand: 'push', + subCommandDescription: 'Provisions only interactions cloud resources with the latest local developments', + subCommandUsage: 'amplify interactions push', + subCommandFlags: [], + }, + { + subCommand: 'remove', + subCommandDescription: 'Removes interactions resources from your local backend', + subCommandUsage: 'amplify interactions remove', + subCommandFlags: [], + }, + ], + }, + { + command: 'predictions', + commandDescription: 'Configure predictions resources for your Amplify project', + commandUsage: 'amplify predictions ', + commandFlags: [], + subCommands: [ + { + subCommand: 'add', + subCommandDescription: 'Takes you through a CLI flow to add a predictions resource to your local backend', + subCommandUsage: 'amplify predictions add', + subCommandFlags: [], + }, + { + subCommand: 'remove', + subCommandDescription: 'Removes predictions resource from your local backend', + subCommandUsage: 'amplify predictions remove', + subCommandFlags: [], + }, + { + subCommand: 'update', + subCommandDescription: 'Takes you through steps in the CLI to update an predictions resource', + subCommandUsage: 'amplify predictions update', + subCommandFlags: [], + }, + { + subCommand: 'console', + subCommandDescription: 'Opens a web console to view your predictions resource', + subCommandUsage: 'amplify predictions console', + subCommandFlags: [], + }, + { + subCommand: 'push', + subCommandDescription: 'Provisions cloud resources with the latest local changes', + subCommandUsage: 'amplify predictions push', + subCommandFlags: [], + }, + ], + }, +]; diff --git a/packages/amplify-cli-core/src/help/help-helpers.ts b/packages/amplify-cli-core/src/help/help-helpers.ts new file mode 100644 index 00000000000..f2ecc0caa7d --- /dev/null +++ b/packages/amplify-cli-core/src/help/help-helpers.ts @@ -0,0 +1,250 @@ +import { $TSContext, $TSAny } from '../types'; +import { printer } from 'amplify-prompts'; +import chalk from 'chalk'; + +export type CommandFlagInfo = { + short: string; + long: string; + flagDescription: string; +}; + +export type SubCommandInfo = { + subCommand: string; + subCommandDescription: string; + subCommandUsage: string; + subCommandFlags: Array; +}; + +export type CommandInfo = { + command: string; + commandDescription: string; + commandUsage: string; + commandFlags: Array; + subCommands: Array; +}; + +const SPACE = ' '; +const DEFAULT_COLUMN_SEP_SPACING = 2; +const DEFAULT_INDENT_SPACING = 2; +const DEFAULT_COLUMN_SEP = SPACE.repeat(DEFAULT_COLUMN_SEP_SPACING); +const DEFAULT_INDENT = SPACE.repeat(DEFAULT_INDENT_SPACING); +const MAX_LINE_LENGTH = 80; + +const USAGE = 'USAGE'; +const DESCRIPTION = 'DESCRIPTION'; +const COMMANDS = 'COMMANDS'; +const FLAGS = 'FLAGS'; +const LEARN_MORE = 'LEARN MORE'; +const SUBCOMMANDS = 'SUBCOMMANDS'; +const TAG_LINE = [ + 'The Amplify Command Line Interface (CLI) is a unified toolchain to', + 'create, integrate, and manage the AWS cloud services for your app.', +]; +const DEFAULT_LINK = 'https://docs.amplify.aws/cli/'; + +const FLAG_DELIMITER = ' | '; +const FLAG_PREFIX_SHORT = '-'; +const FLAG_PREFIX_LONG = '--'; + +const printHeaderText = (text: string) => printer.info(chalk.blue.bold(text)); +const printBodyText = (text: string) => printer.info(text); +const printCategoryMessage = () => { + printBodyText(DEFAULT_INDENT + 'Where is one of: notifications, api, auth, custom, storage,'); + printBodyText(DEFAULT_INDENT + 'analytics, function, geo, hosting, interactions, predictions, xr'); + printer.blankLine(); +}; + +const AMPLIFY_CLI_DOCS_URL = `https://docs.amplify.aws/cli`; +const HEADLESS_DOCS_LINK = `${AMPLIFY_CLI_DOCS_URL}/usage/headless`; +const getDocsLinkForCommand = (commandName: string) => `${AMPLIFY_CLI_DOCS_URL}/commands/${commandName}`; + +export function lookUpCommand(commandsInfo: Array, commandName: string): CommandInfo | undefined { + return commandsInfo.find(element => element.command === commandName); +} + +export function lookUpSubcommand( + commandsInfo: Array, + commandName: string, + subcommandName: string, +): SubCommandInfo | undefined { + const command = lookUpCommand(commandsInfo, commandName); + if (command === undefined) { + return undefined; + } + return command.subCommands.find(element => element.subCommand === subcommandName); +} + +export function parseHelpCommands(input: $TSAny, commandsInfo: Array) { + // depending on the commands and the order of commands, information is stored in different fields of Input + let specifiedCommands = { command: '', subCommand: '' }; + // get all allowed commands/subcommands from json object + const acceptableCommands: Array = []; + commandsInfo.forEach(command => acceptableCommands.push(command.command)); + commandsInfo.forEach(command => command.subCommands.forEach(subCommand => acceptableCommands.push(subCommand.subCommand))); + const hasSubcommands = input.subCommands && Array.isArray(input.subCommands) && input.subCommands.length; // check if subcommands exist + if (hasSubcommands) { + specifiedCommands = { command: input.subCommands[0], subCommand: '' }; // if just 1 subcommand, set that as command + if (input.subCommands.length === 1) { + if (input.options) { + // check if subcommands are in options field + const subcommandsInOptions = acceptableCommands.filter(i => input.options.hasOwnProperty(i)); + if (subcommandsInOptions && subcommandsInOptions.length === 1) { + specifiedCommands = { command: input.subCommands[0], subCommand: subcommandsInOptions[0] }; + } + } + } else if (input.subCommands.length === 2) { + specifiedCommands = { command: input.subCommands[0], subCommand: input.subCommands[1] }; + } + } + return specifiedCommands; +} + +function getHelpFlagRow(flagObject: CommandFlagInfo): [string, string] { + const has_short = flagObject.short.length > 0; + const has_long = flagObject.long.length > 0; + let columns: [string, string]; + if (has_short && has_long) { + columns = [FLAG_PREFIX_SHORT + flagObject.short + FLAG_DELIMITER + FLAG_PREFIX_LONG + flagObject.long, flagObject.flagDescription]; + } else if (has_short) { + columns = [FLAG_PREFIX_SHORT + flagObject.short, flagObject.flagDescription]; + } else { + columns = [FLAG_PREFIX_LONG + flagObject.long, flagObject.flagDescription]; + } + + if (flagObject.long === 'headless') { + columns[1] += ` (see ${HEADLESS_DOCS_LINK})`; + } + + return columns; +} + +function printColumns(rowsArray: Array<[string, string]>, minColumnSeparatingSpaces: number, indentation = 2) { + const longestFirstColLength = Math.max(...rowsArray.map(row => row[0].length)); + rowsArray.forEach(function(row) { + const separatingSpaces = longestFirstColLength - row[0].length + minColumnSeparatingSpaces; + printBodyText(SPACE.repeat(indentation) + row[0] + SPACE.repeat(separatingSpaces) + row[1]); + }); +} + +function printGenericHelp(context: $TSContext, commandsInfo: Array, defaultNumTabs = 1, extraTabLengthThreshold = 5) { + TAG_LINE.forEach(line => printBodyText(line)); + printer.blankLine(); + + printHeaderText(USAGE); + printBodyText(DEFAULT_INDENT + 'amplify [flags]'); + printer.blankLine(); + + printHeaderText(COMMANDS); + const commandRows: Array<[string, string]> = commandsInfo.map(commandObject => [commandObject.command, commandObject.commandDescription]); + printColumns(commandRows, DEFAULT_COLUMN_SEP_SPACING); + printer.blankLine(); + + printHeaderText(FLAGS); + printBodyText(DEFAULT_INDENT + '-h' + FLAG_DELIMITER + '--help' + DEFAULT_COLUMN_SEP + 'Show help for a command'); + printer.blankLine(); + + printHeaderText(LEARN_MORE); + printBodyText(DEFAULT_INDENT + 'Visit ' + DEFAULT_LINK); + printer.blankLine(); +} + +function printCommandSpecificHelp( + context: $TSContext, + commandsInfo: Array, + commandName: string, + defaultNumTabs = 1, + extraTabLengthThreshold = 5, +) { + const command = lookUpCommand(commandsInfo, commandName); + if (command === undefined) { + printGenericHelp(context, commandsInfo, defaultNumTabs, extraTabLengthThreshold); + return; + } + + printHeaderText(USAGE); + printBodyText(DEFAULT_INDENT + command.commandUsage); + + printer.blankLine(); + if (command.commandUsage.includes('')) { + printCategoryMessage(); + } + + printHeaderText(DESCRIPTION); + printBodyText(DEFAULT_INDENT + command.commandDescription); + printer.blankLine(); + + if (command.subCommands.length > 0) { + printHeaderText(SUBCOMMANDS); + const subCommandRows: Array<[string, string]> = command.subCommands.map(subCommandObject => [ + subCommandObject.subCommand, + subCommandObject.subCommandDescription, + ]); + printColumns(subCommandRows, DEFAULT_COLUMN_SEP_SPACING); + printer.blankLine(); + const subcommandOrCategory = command.commandUsage.includes('') ? '' : ''; + printBodyText(DEFAULT_INDENT + 'Use "amplify ' + command.command + ' ' + subcommandOrCategory + ' -h" to see subcommand-specific help'); + printer.blankLine(); + } + + if (command.commandFlags.length > 0) { + printHeaderText(FLAGS); + const flagRows: Array<[string, string]> = command.commandFlags.map(flagObject => getHelpFlagRow(flagObject)); + printColumns(flagRows, DEFAULT_COLUMN_SEP_SPACING); + printer.blankLine(); + } + + printHeaderText(LEARN_MORE); + printBodyText(DEFAULT_INDENT + getDocsLinkForCommand(commandName)); + printer.blankLine(); +} + +function printSubcommandSpecificHelp( + context: $TSContext, + commandsInfo: Array, + commandName: string, + subcommandName: string, + defaultNumTabs = 1, + extraTabLengthThreshold = 5, +) { + const subCommand = lookUpSubcommand(commandsInfo, commandName, subcommandName); + if (subCommand === undefined) { + const command = lookUpCommand(commandsInfo, commandName); + if (command === undefined) { + printGenericHelp(context, commandsInfo, defaultNumTabs, extraTabLengthThreshold); + return; + } else { + printCommandSpecificHelp(context, commandsInfo, command.command, defaultNumTabs, extraTabLengthThreshold); + return; + } + } + + printHeaderText(USAGE); + printBodyText(DEFAULT_INDENT + subCommand.subCommandUsage); + printer.blankLine(); + + printHeaderText(DESCRIPTION); + printBodyText(DEFAULT_INDENT + subCommand.subCommandDescription); + printer.blankLine(); + + if (Object.keys(subCommand.subCommandFlags).length > 0) { + printHeaderText(FLAGS); + const flagRows: Array<[string, string]> = subCommand.subCommandFlags.map(flagObject => getHelpFlagRow(flagObject)); + printColumns(flagRows, DEFAULT_COLUMN_SEP_SPACING); + printer.blankLine(); + } + + printHeaderText(LEARN_MORE); + printBodyText(DEFAULT_INDENT + getDocsLinkForCommand(commandName)); + printer.blankLine(); +} + +export function runHelp(context: $TSContext, commandsInfo: Array) { + const specifiedCommands = parseHelpCommands(context.input, commandsInfo); + if (specifiedCommands.command.length > 0 && specifiedCommands.subCommand.length > 0) { + printSubcommandSpecificHelp(context, commandsInfo, specifiedCommands.command, specifiedCommands.subCommand); + } else if (specifiedCommands.command.length > 0) { + printCommandSpecificHelp(context, commandsInfo, specifiedCommands.command); + } else { + printGenericHelp(context, commandsInfo); + } +} diff --git a/packages/amplify-cli-core/src/help/index.ts b/packages/amplify-cli-core/src/help/index.ts new file mode 100644 index 00000000000..c1079e68178 --- /dev/null +++ b/packages/amplify-cli-core/src/help/index.ts @@ -0,0 +1,2 @@ +export * from './help-helpers'; +export * from './commands-info'; \ No newline at end of file diff --git a/packages/amplify-cli-core/src/index.ts b/packages/amplify-cli-core/src/index.ts index c95a5e84cc3..b55df11946e 100644 --- a/packages/amplify-cli-core/src/index.ts +++ b/packages/amplify-cli-core/src/index.ts @@ -32,3 +32,4 @@ export * from './errors/amplify-error'; export * from './errors/amplify-fault'; export * from './errors/amplify-exception'; export * from './errors/project-not-initialized-error'; +export * from './help'; \ No newline at end of file diff --git a/packages/amplify-cli-npm/index.ts b/packages/amplify-cli-npm/index.ts index ebd969dcb21..5cbeeacd93c 100644 --- a/packages/amplify-cli-npm/index.ts +++ b/packages/amplify-cli-npm/index.ts @@ -17,3 +17,4 @@ export const install = async (): Promise => { }; // force minor version bump to 10.6 +// diff --git a/packages/amplify-cli/src/commands/help.ts b/packages/amplify-cli/src/commands/help.ts index 6201ab98b06..07b3c1fe3b4 100644 --- a/packages/amplify-cli/src/commands/help.ts +++ b/packages/amplify-cli/src/commands/help.ts @@ -1,9 +1,9 @@ import { $TSContext } from 'amplify-cli-core'; -import { showAllHelp } from '../extensions/amplify-helpers/show-all-help'; +import { runHelp, commandsInfo } from 'amplify-cli-core'; /** * displays amplify help menu */ export const run = async (context: $TSContext): Promise => { - showAllHelp(context); + runHelp(context, commandsInfo); }; diff --git a/packages/amplify-cli/src/commands/plugin/help.ts b/packages/amplify-cli/src/commands/plugin/help.ts index 5000a0f4082..d679504b36e 100644 --- a/packages/amplify-cli/src/commands/plugin/help.ts +++ b/packages/amplify-cli/src/commands/plugin/help.ts @@ -1,48 +1,6 @@ -import { Context } from '../../domain/context'; +import { $TSContext } from 'amplify-cli-core'; +import { runHelp, commandsInfo } from 'amplify-cli-core'; -export const run = (context: Context) => { - context.print.info(''); - - const commands = [ - { - name: 'init', - description: 'Scaffolds a skeleton Amplify CLI plugin.', - }, - { - name: 'configure', - description: 'Walkthrough to configure Amplify CLI plugin options, e.g. directories where the CLI scans for plugins.', - }, - { - name: 'list', - description: 'Lists general plugin information for, e.g. list of active plugins.', - }, - { - name: 'scan', - description: 'Explicitly starts a scan/search for new and existing plugins.', - }, - { - name: 'add', - description: 'Explicitly adds a plugin for the Amplify CLI to use.', - }, - { - name: 'remove', - description: ' Explicitly removes a plugin from the Amplify CLI.', - }, - { - name: 'verify', - description: 'Verifies if a plugin package/directory is a valid Amplify CLI plugin.', - }, - { - name: 'help', - description: 'Prints out the a help message for the plugin command.', - }, - ]; - const tableOptions: any = []; - for (let i = 0; i < commands.length; i += 1) { - tableOptions.push([commands[i].name, commands[i].description]); - } - context.print.info('Subcommands for amplify plugin:'); - context.print.info(''); - context.print.table(tableOptions, { format: 'default' }); - context.print.info(''); +export const run = (context: $TSContext) => { + runHelp(context, commandsInfo); }; diff --git a/packages/amplify-cli/src/input-manager.ts b/packages/amplify-cli/src/input-manager.ts index 019ad9a1df3..b39c51d7472 100644 --- a/packages/amplify-cli/src/input-manager.ts +++ b/packages/amplify-cli/src/input-manager.ts @@ -6,6 +6,7 @@ import { getPluginsWithName, getAllPluginNames } from './plugin-manager'; import { InputVerificationResult } from './domain/input-verification-result'; import { pathManager, stateManager } from 'amplify-cli-core'; import { insertAmplifyIgnore } from './extensions/amplify-helpers/git-manager'; +import { runHelp, commandsInfo } from 'amplify-cli-core'; export function getCommandLineInput(pluginPlatform: PluginPlatform): Input { const result = new Input(process.argv); @@ -63,6 +64,40 @@ export function getCommandLineInput(pluginPlatform: PluginPlatform): Input { return result; } +function preserveHelpInformation(input: Input): Input { + const subCommands = input.subCommands ? input.subCommands : []; + // preserve non-help command in subcommands + if (input.command && input.command.toLowerCase() !== constants.HELP) { + subCommands.unshift(input.command.toLocaleLowerCase()); + } + + const hasLongHelpOption = typeof input.options?.[constants.HELP] === 'string'; + const hasShortHelpOption = typeof input.options?.[constants.HELP_SHORT] === 'string'; + // prevent information in help option from being overwritten to true by saving it in subcommands + if (hasLongHelpOption) { + subCommands.push(input.options?.[constants.HELP] as string); + } else if (hasShortHelpOption) { + subCommands.push(input.options?.[constants.HELP_SHORT] as string); + } + + // preserve command information in plugin field + if (input.plugin && input.plugin !== 'core') { + const isCommandPreceedingPluginName = subCommands?.length && input.argv.indexOf(input.plugin) > input.argv.indexOf(subCommands[0]); + if (isCommandPreceedingPluginName) { + subCommands.push(input.plugin); + } else { + subCommands.unshift(input.plugin); + } + } + if (input.options) { + input.options[constants.HELP] = true; + delete input.options[constants.HELP_SHORT]; + } + input.command = constants.HELP; + input.subCommands = subCommands; + return input; +} + function normalizeInput(input: Input): Input { // -v --version => version command // -h --help => help command @@ -74,8 +109,7 @@ function normalizeInput(input: Input): Input { } if (input.options[constants.HELP] || input.options[constants.HELP_SHORT]) { - input.options[constants.HELP] = true; - delete input.options[constants.HELP_SHORT]; + preserveHelpInformation(input); } if (input.options[constants.YES] || input.options[constants.YES_SHORT]) { diff --git a/packages/amplify-console-hosting/src/commands/help.js b/packages/amplify-console-hosting/src/commands/help.js index 616a201e736..812423d3863 100644 --- a/packages/amplify-console-hosting/src/commands/help.js +++ b/packages/amplify-console-hosting/src/commands/help.js @@ -1,14 +1,7 @@ +import { runHelp, commandsInfo } from 'amplify-cli-core'; + async function run(context) { - // print out the help message of your plugin - context.print.info('amplify hosting serve: Opens your deployed site'); - context.print.info(''); - context.print.info('amplify hosting configure: Set up custom domains, redirects, password protection, and more via the Amplify Console.'); - context.print.info(''); - context.print.info( - 'amplify publish: Publishes changes to manually deployed apps. For continuous deployment, please push to your git branch to deploy updates.', - ); - context.print.info(''); - context.print.info('amplify hosting remove: Remove hosting from you app.'); + runHelp(context, commandsInfo); } module.exports = { diff --git a/packages/amplify-util-mock/src/commands/mock/help.ts b/packages/amplify-util-mock/src/commands/mock/help.ts index 4a3aafcd139..adb7dc15830 100644 --- a/packages/amplify-util-mock/src/commands/mock/help.ts +++ b/packages/amplify-util-mock/src/commands/mock/help.ts @@ -1,24 +1,5 @@ -import { $TSContext } from 'amplify-cli-core'; - -export const name = 'help'; +import { $TSContext, runHelp, commandsInfo } from 'amplify-cli-core'; export const run = (context: $TSContext) => { - const header = `amplify ${name} [subcommand]\nDescription:\nMock resources locally`; - - const commands = [ - { - name: 'storage', - description: 'Run Storage Mock Endpoint', - }, - { - name: 'api', - description: 'Run GraphQL API Mock Endpoint', - }, - { - name: 'function ', - description: 'Invoke Function locally', - }, - ]; - context.amplify.showHelp(header, commands); - return; + runHelp(context, commandsInfo); };