Salesforce CLI Plug-In Developer Guide

Salesforce, Winter ’22

@salesforcedocs Last updated: July 21, 2021 © Copyright 2000–2021 salesforce.com, inc. All rights reserved. Salesforce is a registered trademark of salesforce.com, inc., as are other names and marks. Other marks appearing herein may be trademarks of their respective owners. CONTENTS

Salesforce CLI Plug-In Developer Guide ...... 1 Salesforce CLI Plug-Ins ...... 1 Salesforce CLI Architecture ...... 3 Get Started with Salesforce CLI Plug-In Generation ...... 5 Naming and Messages for Salesforce CLI Plug-Ins ...... 7 Customize Your Salesforce CLI Plug-In ...... 13 Test Your Salesforce CLI Plug-In ...... 32 Debug Your Salesforce CLI Plug-In ...... 33 Best Practices for Salesforce CLI Plug-In Development ...... 33 Resources for Salesforce CLI Plug-In Development ...... 35

SALESFORCE CLI PLUG-IN DEVELOPER GUIDE

Discover how to develop your own plug-ins for Salesforce CLI. Explore the Salesforce CLI architecture. Learn how to generate a plug-in using Salesforce Plug-In Generator, use Salesforce’s libraries to add functionality to your plug-in, and debug issues. Learn about our suggested style guidelines for naming and messages and our recommended best practices for plug-ins.

Salesforce CLI Plug-Ins A plug-in adds functionality to Salesforce CLI. Some plug-ins are provided by Salesforce and are installed by default when you install the CLI. Some plug-ins, built by Salesforce and others, you install. When you have a requirement that an existing plug-in doesn’t meet, you can build your own using Node.js. Salesforce CLI Architecture Before you get started with adding functionality to Salesforce CLI, let’s take a high-level look at how the CLI and its dependencies and plug-ins work together. Get Started with Salesforce CLI Plug-In Generation Set up your computer for Salesforce CLI plug-in generation, and then generate a plug-in. Naming and Messages for Salesforce CLI Plug-Ins Before you dive into coding, take some time to plan your naming strategy. And it’s never too early to start thinking about the messages you display for your users. Customize Your Salesforce CLI Plug-In To customize your plug-in, duplicate and update the generated files. You can customize your commands’ parameters, properties, error-handling, and output. Test Your Salesforce CLI Plug-In While you’re coding and customizing your plug-in, be sure to write associated tests for each new feature to ensure that it’s working as you expect. In your test suites, include both unit tests and more complex integration, smoke, and end-to-end tests. To help you with latter, we’ve created a library of testing utilities that you add to your environment as a Yarn developer dependency. Debug Your Salesforce CLI Plug-In We recommend using the Visual Studio Code (VS Code) editor, with Salesforce Extensions for VS Code, for your plug-in development. Included in the .vscode directory of Salesforce Plug-In Generator’s generated plug-ins is a launch.json config file. This config file allows you to attach a debugger to the Node process when running your commands. Best Practices for Salesforce CLI Plug-In Development We suggest that you follow these patterns when developing plug-ins for Salesforce CLI. Resources for Salesforce CLI Plug-In Development Bookmark these resources so that you can refer to them as you develop plug-ins.

Salesforce CLI Plug-Ins

A plug-in adds functionality to Salesforce CLI. Some plug-ins are provided by Salesforce and are installed by default when you install the CLI. Some plug-ins, built by Salesforce and others, you install. When you have a requirement that an existing plug-in doesn’t meet, you can build your own using Node.js.

1 Salesforce CLI Plug-In Developer Guide What Is a Salesforce CLI Plug-In?

What Is a Salesforce CLI Plug-In? A plug-in adds commands or features to Salesforce CLI. For example, the aggregate salesforcedx plug-in provides the sfdx force commands. Salesforce CLI plug-ins are npm (Node.js package manager) packages. Node.js is a JavaScript runtime environment that supports execution outside of a browser. If you prefer strongly typed languages, don’t worry: We recommend that you use TypeScript, which transpiles to JavaScript. Salesforce Plug-In Generator’s sample plug-in uses TypeScript. Why Create a Salesforce CLI Plug-In? The Salesforce CLI core plug-ins provide commands and functionality to meet common needs that customers and partners have. But each team often has specific needs. That’s why Salesforce CLI is extensible. Even at Salesforce, different teams have different requirements. Salesforce teams create plug-ins, Salesforce employees create plug-ins as side projects, and Trailblazers in the Salesforce community create and share plug-ins. Knowledge and Skills for Salesforce CLI Plug-In Development Building a Salesforce CLI plug-in requires different knowledge and skills than most Salesforce development. Before you dive too deeply into plug-in development, familiarize yourself with these areas.

What Is a Salesforce CLI Plug-In? A plug-in adds commands or features to Salesforce CLI. For example, the aggregate salesforcedx plug-in provides the sfdx force commands. Salesforce CLI plug-ins are npm (Node.js package manager) packages. Node.js is a JavaScript runtime environment that supports execution outside of a browser. If you prefer strongly typed languages, don’t worry: We recommend that you use TypeScript, which transpiles to JavaScript. Salesforce Plug-In Generator’s sample plug-in uses TypeScript. We recommend using TypeScript instead of JavaScript because it’s strongly typed and thus better suited to large projects. The stronger types give a better IDE experience and reduce common programming mistakes that are easy to make but hard to debug. A user installs a plug-in by running sfdx plugins:install PLUGINNAME , where PLUGINNAME is an npm package on npmjs.com. To see your installed plug-ins and their versions, run sfdx plugins. To see which versions of the default Salesforce-provided plug-ins are installed on your computer, run sfdx plugins --core.

Why Create a Salesforce CLI Plug-In? The Salesforce CLI core plug-ins provide commands and functionality to meet common needs that customers and partners have. But each team often has specific needs. That’s why Salesforce CLI is extensible. Even at Salesforce, different teams have different requirements. Salesforce teams create plug-ins, Salesforce employees create plug-ins as side projects, and Trailblazers in the Salesforce community create and share plug-ins. Perhaps you have specific tools that you want to use for code analysis as part of your CI/CD automation process. You could build a plug-in that runs your bespoke code-analysis tool. Creating a plug-in provides the benefits of a consistent user experience and a common framework. One Trailblazer plug-in was developed by the Salesforce’s Tableau CRM team. The team knew that creating environments to develop, test, and share Tableau CRM apps, and converting those apps to templates for distribution, can be time-consuming. So they built the @salesforce/analytics plug-in to use scratch orgs to develop an app in Tableau CRM Analytics Studio. The plug-in imports data to datasets, builds dashboards, and connects everything. The team wrote commands that convert the app to templates and download the template JSON files for further refinement. Thanks to these commands, you can also quickly share the results of your work with other developers using a version control system. Your colleagues can push the template JSON files to their own scratch orgs for further testing and development. Because so much Salesforce functionality is surfaced in , the sky’s the limit as to what you can build. With Salesforce Plug-In Generator, the @salesforce/core library, and the @salesforce/command package, you have the tools you need to get started with Salesforce CLI plug-in development.

2 Salesforce CLI Plug-In Developer Guide Knowledge and Skills for Salesforce CLI Plug-In Development

Knowledge and Skills for Salesforce CLI Plug-In Development Building a Salesforce CLI plug-in requires different knowledge and skills than most Salesforce development. Before you dive too deeply into plug-in development, familiarize yourself with these areas. • TypeScript (or at least JavaScript, which TypeScript transpiles to) • Node.js • npm (Node.js Package Manager) • Yarn in modern JavaScript build tools • Salesforce APIs

Salesforce CLI Architecture

Before you get started with adding functionality to Salesforce CLI, let’s take a high-level look at how the CLI and its dependencies and plug-ins work together.

Salesforce CLI Salesforce CLI is an npm package called sfdx-cli. You run it on your local machine or continuous integration (CI) system. It supports the installation of custom plug-ins. Most of the core functionality that Salesforce provides comes from plug-ins. salesforcedx Core Plug-Ins The sfdx force commands that you know and love are part of salesforcedx, an aggregate plug-in. @salesforce/core Package The @salesforce/core library provides client-side management of Salesforce DX projects, org authentication, connections to Salesforce APIs, and other utilities. Much of the core functionality that powers the salesforcedx plug-ins comes from this library. You can use this functionality in your plug-ins, too. @salesforce/command Package The @salesforce/command npm package provides the base command class for Salesforce CLI. Extend this class to give your commands access to common Salesforce CLI parameters, a logger, CLI output formatting, scratch orgs, Dev Hubs, and the user’s Salesforce DX project context. Open CLI Framework (oclif) oclif is an open-source CLI framework developed by Heroku. It powers Heroku CLI, parts of Salesforce CLI, the salesforcedx aggregate plug-in, and Salesforce Plug-In Generator. The plug-ins that you generate with Salesforce Plug-In Generator use the oclif format and can take advantage of the many features that the framework offers. In addition to enabling plug-in creation, this framework enables developers to create their own standalone CLI applications. @salesforce/plugin-generator Salesforce Plug-In Generator, available as a scoped npm package on npmjs, is also a CLI plug-in. An npm package scope is like a namespace. (The commands in the salesforcedx plug-in are in the force namespace, so they start with sfdx force. The commands in @salesforce/plugin-generator start with sfdx plugins.) With one Salesforce CLI command, Salesforce Plug-In Generator scaffolds a TypeScript project that contains everything you need to get started with building your own CLI plug-in.

Salesforce CLI Salesforce CLI is an npm package called sfdx-cli. You run it on your local machine or continuous integration (CI) system. It supports the installation of custom plug-ins. Most of the core functionality that Salesforce provides comes from plug-ins.

3 Salesforce CLI Plug-In Developer Guide salesforcedx Core Plug-Ins

Use Salesforce CLI for many of your development tasks, such as authorizing to any type of org, creating scratch orgs, synchronizing source code between your scratch orgs and VCS, and running tests. All Salesforce CLI commands start with sfdx. To see which sfdx-cli and salesforcedx plug-in versions you’re using, run sfdx plugins --core. To see the available sets of commands—also known as namespaces or top-level topics—run sfdx --help. To see a list of all available commands in an easy-to-read format, run sfdx commands --json To get started, make sure to review the Before You Begin and Salesforce CLI Configuration and Tips sections in the Salesforce CLI Setup Guide. Salesforce CLI is based on Heroku’s oclif engine. For details about installing the CLI with npm or yarn, and about the CLI’s oclif dependencies, check out the sfdx-cli package details.

salesforcedx Core Plug-Ins The sfdx force commands that you know and love are part of salesforcedx, an aggregate plug-in. To see which salesforcedx version you’re using, run sfdx plugins --core.

@salesforce/core Package The @salesforce/core library provides client-side management of Salesforce DX projects, org authentication, connections to Salesforce APIs, and other utilities. Much of the core functionality that powers the salesforcedx plug-ins comes from this library. You can use this functionality in your plug-ins, too. @salesforce/core is an npm package. You can find it at https://www.npmjs.com/package/@salesforce/core. Its source lives at https://github.com/forcedotcom/sfdx-core. Details about the many commands in the library are in its reference documentation.

@salesforce/command Package The @salesforce/command npm package provides the base command class for Salesforce CLI. Extend this class to give your commands access to common Salesforce CLI parameters, a logger, CLI output formatting, scratch orgs, Dev Hubs, and the user’s Salesforce DX project context. The @salesforce/command class extends @oclif/command and is available within plug-ins generated by Salesforce Plug-In Generator. You can find the @salesforce/command package at https://www.npmjs.com/package/@salesforce/command. Its source lives at https://github.com/forcedotcom/cli-packages/tree/develop/packages/command.

Open CLI Framework (oclif) oclif is an open-source CLI framework developed by Heroku. It powers Heroku CLI, parts of Salesforce CLI, the salesforcedx aggregate plug-in, and Salesforce Plug-In Generator. The plug-ins that you generate with Salesforce Plug-In Generator use the oclif format and can take advantage of the many features that the framework offers. In addition to enabling plug-in creation, this framework enables developers to create their own standalone CLI applications. For details about oclif, see https://oclif.io/. For information about the features that oclif offers, see https://oclif.io/docs/features. For the oclif API reference, see https://oclif.io/docs/commands.

4 Salesforce CLI Plug-In Developer Guide @salesforce/plugin-generator

@salesforce/plugin-generator Salesforce Plug-In Generator, available as a scoped npm package on npmjs, is also a CLI plug-in. An npm package scope is like a namespace. (The commands in the salesforcedx plug-in are in the force namespace, so they start with sfdx force. The commands in @salesforce/plugin-generator start with sfdx plugins.) With one Salesforce CLI command, Salesforce Plug-In Generator scaffolds a TypeScript project that contains everything you need to get started with building your own CLI plug-in. Like the salesforcedx plug-in, @salesforce/plugin-generator is bundled with the Salesforce CLI installation. You can check your installed version by running this command:

sfdx plugins --core

For details about @salesforce/plugin-generator, see https://www.npmjs.com/package/@salesforce/plugin-generator.

Get Started with Salesforce CLI Plug-In Generation

Set up your computer for Salesforce CLI plug-in generation, and then generate a plug-in.

Prepare Your Computer for Salesforce CLI Plug-In Development Before you use Salesforce Plug-In Generator to create a plug-in, set up these prerequisites. Scaffold a Salesforce CLI Plug-In Use Salesforce Plug-In Generator to create your own plug-in for Salesforce CLI.

Prepare Your Computer for Salesforce CLI Plug-In Development Before you use Salesforce Plug-In Generator to create a plug-in, set up these prerequisites. 1. Install or update Node.js. To build a Salesforce CLI plug-in, you need the latest long-term support (LTS) version of Node.js. If you’re new to Node.js development, we suggest that you use nvm (Node Version Manager) to install Node.js. Creationix provides an installation script to install or update nvm. To check your Node.js version, run:

node --version

If your node version is earlier than 8 (or if you don’t have Node.js installed), run:

nvm install node

2. Install the Yarn package manager.

npm install -g yarn

3. Install TypeScript target es2017.

npm install -g typescript

Salesforce CLI plug-ins can use JavaScript instead of TypeScript, but the classes in the Salesforce DX Core Library are written in TypeScript.

4. Install or update Salesforce CLI.

5 Salesforce CLI Plug-In Developer Guide Scaffold a Salesforce CLI Plug-In

If you don’t have Salesforce CLI installed on your computer, see Install the Salesforce CLI in the Salesforce CLI Setup Guide. The @salesforce/plugin-generator plug-in is included with Salesforce CLI v6.7.1 and later. To check your Salesforce CLI version, run:

sfdx --version

If your sfdx-cli version is earlier than 6.7.1, run:

sfdx update

Scaffold a Salesforce CLI Plug-In Use Salesforce Plug-In Generator to create your own plug-in for Salesforce CLI. 1. Run the plug-in generation command, replacing yourPluginName with the name of your plug-in.

sfdx plugins:generate yourPluginName

You are prompted for information to populate your new plug-in unless you include the --defaults flag. You can answer the questions or press Enter to use a default value. The generator scaffolds a new Salesforce CLI plug-in and installs the plug-in's npm package dependencies.

2. Change to the new plug-in directory.

cd yourPluginName

3. The new plug-in contains an example hello:org command. You can find the code for that command at yourPluginName/src/commands/hello/org.ts. Open the file, and see what it contains. For example, to open the file in Visual Studio Code, run:

code src/commands/hello/org.ts

The hello:org command imports and extends classes from @salesforce/command. When you add your own commands, you extend @salesforce/command.

4. To run the commands in your in-development plug-in from the directory that your code lives in, precede the commands with bin/run. Look at the --help (-h) output for the sample hello:org command.

bin/run hello:org -h

5. Customize your plug-in. (This step might take a while!) To test your functionality along the way, run:

bin/run yournamespace:yourcommand

6. When you’re ready (see the Tip) to test-drive your plug-in, link your in-development plug-in to Salesforce CLI.

sfdx plugins:link

Tip: Save this step until you’re confident that you want to move forward with the plug-in you’re working on. If you decide after running sfdx plugins:link that you want to uninstall your plug-in, run sfdx plugins:uninstall yourPluginName. If your linked plug-in doesn’t compile, sfdx plugins:uninstall yourPluginName will work only after you fix the compilation errors.

6 Salesforce CLI Plug-In Developer Guide Naming and Messages for Salesforce CLI Plug-Ins

This command installs your plug-in in Salesforce CLI by creating a symlink to your yourPluginName directory. After you link your plug-in, you can run your commands without using bin/run. For example:

sfdx hello:org -h

7. For bonus points, change directories to a Salesforce DX project that has a default scratch org. Run the sample hello:org command (or your favorite command from your plug-in).

sfdx hello:org

SEE ALSO: Prepare Your Computer for Salesforce CLI Plug-In Development

Naming and Messages for Salesforce CLI Plug-Ins

Before you dive into coding, take some time to plan your naming strategy. And it’s never too early to start thinking about the messages you display for your users. The core Salesforce-provided commands have this structure.

namespace:topic:optionalsubtopic:command

oclif supports hyphens and capital letters in command and parameter names, but not in namespaces or topic names. However, names of the core Salesforce-provided commands and their parameters are all lowercase (except for some parameters’ short names), with no hyphens. To reduce cognitive load for your users, choose a capitalization and hyphenation strategy, and follow it consistently. We recommend that all plug-ins follow our core plug-in style guidelines. That way, your users don’t need to learn a new naming system each time they adopt a new plug-in.

Namespace Names The namespace for your plug-in is the first part of all your commands. For example, the salesforcedx aggregate plug-in uses the force namespace, so all its commands start with force:. Namespace is another word for top-level topic; a topic is a set of commands. Topic and Command Names After you choose a namespace, you’re ready to start naming your topics and commands. The names of your command definition files and the directories they live in determine your command and topic names. Parameter Names Each parameter has a long name and a short name. Set these values in your command definition file. Command and Parameter Descriptions Use clear, concise descriptions so that users can easily understand what your commands and parameters do. Store your descriptions in files within the yourPluginName/messages/ directory. Create a messages file for each command. Examples and Help Text Use the examples to teach users how to use your commands. Store your help text in the files within the yourPluginName/messages/ directory where you store your description values. Error Messages Use an error message to tell users how to recover from the situation that caused the error. Store your error messages in the same files within the yourPluginName/messages/ directory where you store your description values and help text.

7 Salesforce CLI Plug-In Developer Guide Namespace Names

Namespace Names The namespace for your plug-in is the first part of all your commands. For example, the salesforcedx aggregate plug-in uses the force namespace, so all its commands start with force:. Namespace is another word for top-level topic; a topic is a set of commands. Choose a namespace that’s short, easy to type, and unique to your company or project. Pick a namespace that describes the functionality you plan to provide without being so specific that it becomes outdated as your feature set grows. Don’t add commands to the Salesforce-provided namespaces, such as force and plugins. Instead, create your own namespace. Otherwise, your commands can disappear or stop working when we add functionality to the core plug-ins.

Topic and Command Names After you choose a namespace, you’re ready to start naming your topics and commands. The names of your command definition files and the directories they live in determine your command and topic names.

Style Guidelines for Topic and Command Names The topic and optional subtopic names in Salesforce-provided commands are nouns. Command names are verbs that act on the preceding noun. For example, the force:org:create command has a namespace of force, a topic of org, and a command name of create. The verb create acts on the noun org. The force:apex:class:create command has a namespace of force, a topic of apex, a subtopic of class, and a command name of create. The verb create acts on the noun class, which is a category within apex.

How to Set Topic and Command Names A topic name is set by the name of the directory where the topic’s command definition files live. For example, commands for the hello topic in the sample plug-in live in the yourPluginName/src/commands/hello directory. Because your namespace is a top-level topic, set the name of the top-level directory within yourPluginName/src/commands/ to your namespace. To create a subtopic, nest its directory in your yourPluginName/src/commands/ directory. For example, if you wanted to rename the sample hello:org command to hello:my:org, you could add a yourPluginName/src/commands/hello/my/ directory and move the org.ts file into that directory (from yourPluginName/src/commands/hello/) . In this case, hello is the topic, my is the subtopic, and org is the command. (But, that command doesn’t follow the command naming guidelines—and neither does hello:org!) The name of your command definition file sets your command name. For example, the definition file for the hello:org command is yourPluginName/src/commands/hello/org.ts. Renaming your command definition file renames your command.

Parameter Names Each parameter has a long name and a short name. Set these values in your command definition file.

Style Guidelines for Parameter Names In the core Salesforce CLI plug-ins, parameter long names are lowercase. When a user runs a command, the long names are prefixed with double hyphens. Short names are a single character, prefixed with one hyphen. Long Names

8 Salesforce CLI Plug-In Developer Guide Parameter Names

For parameters that accept a value, choose a long name that briefly describes the value. For example, the auth:web:login command’s --instanceurl parameter accepts the login URL of the Salesforce instance that the org lives on. For parameters of type flag, which alter the behavior of a command but don’t accept a value, choose a long name that briefly describes the parameter’s effect. For example, the auth:web:login command’s --setdefaultusername parameter sets the authenticated org as the default username that all commands run against. Short Names A short name is typically the first letter of the long name it represents. If that character is unavailable, use the next prominent sound in the long name or the letter that most distinguishes the long name. In the core Salesforce CLI plug-ins, short names are mandatory for required parameters. For optional parameters, we suggest having a short name if the parameter is used frequently, takes a value, or has a long name that’s more than five characters. To minimize the chances that your users accidentally misuse your commands, consider not giving parameters with destructive behavior a short name. Uppercase short names are permitted, but follow these guidelines. • Use the lowercase version of the short name before using the uppercase version of a letter. • Use the lowercase version for the more commonly used parameter, using an uppercase version of the letter for parameters that are used infrequently. • Try to use the uppercase and lowercase versions of a letter for parameters that have related functionality. • For commands that have parameters related to parents and children (for example, packages and package versions), use uppercase letters for parent-related parameters and lowercase letters for child-related parameters. • Try to capitalize letters that are distinguishable as capitalized versus not capitalized. For example, choose -A (-a) and -G (-g) over -S (-s) and -W (-w). Naming Patterns These names are standardized in the core Salesforce CLI plug-ins. ID values Short name: -i Long name: The type of ID, such as --testrunid If the command can accept more than one type of ID, use -i for the parameter that users provide most frequently. File paths Short name: -f Long name: The type of file, such as --csvfile or --definitionfile For files (for example, package.xml), use -x | --manifest. Directory paths Short name: -d Long name: The type of directory, such as --outputdir or --sourcedir Names Short name: -n Long name: The type of name, such as --fieldname sObjects Short name: -s Long name: --sobjecttype

9 Salesforce CLI Plug-In Developer Guide Command and Parameter Descriptions

How to Set Parameter Names Define the names of your parameters in the flagsConfig object in your command definition file. Long names are the names of a flagsConfig attribute, and short names are a char value. For example, Salesforce Plug-In Generator’s sample plug-in includes this code in yourPluginName/src/commands/hello/org.ts. The hello:org command has two custom parameters: -n | --name and -f | --force.

protected static flagsConfig: FlagsConfig = { name: flags.string({char: 'n', description: messages.getMessage('nameFlagDescription')}), force: flags.boolean({char: 'f', description: messages.getMessage('forceFlagDescription')}) };

Command and Parameter Descriptions Use clear, concise descriptions so that users can easily understand what your commands and parameters do. Store your descriptions in files within the yourPluginName/messages/ directory. Create a messages file for each command.

Style Guidelines for Command and Parameter Descriptions • Each parameter has a description attribute. The value of this attribute displays in the terminal when users run -h | --help for a command or topic. To tell users what the command or parameter does, include enough information but keep your descriptions as short as possible to minimize wrapping in narrow terminals. • description values for core Salesforce CLI plug-ins are lowercase, except for proper nouns and acronyms. Semicolons are acceptable to break up a clause, such as skip validation during package version creation; package versions created without validation can’t be promoted. Do not include end punctuation. • For commands, write a description that starts with an imperative verb. For example, the description value for force:org:create is create a scratch org. The description tells users what the command helps them do. • For parameters that accept a value, the description describes the value that the user supplies. For example, the description for the force:org:create command’s --definitionfile parameter is path to a scratch org definition file. • For parameters of type boolean, which alter the behavior of a command but don’t accept a value, the description tells users what the flag makes the command do. Start these descriptions with an imperative verb. For example, the description for the global --json flag is format output as json.

How to Set Command and Parameter Descriptions All command and parameter definitions include a description. To get the value for this description, point to your command’s messages file. For example, the hello:org command’s definition file, yourPluginName/src/commands/hello/org.ts, includes these lines. Pointer to the messages file:

// Initialize Messages with the current plugin directory core.Messages.importMessagesDirectory(__dirname);

// Load the specific messages for this file.

10 Salesforce CLI Plug-In Developer Guide Examples and Help Text

// Messages from @salesforce/command, @salesforce/core, // or any library that is using the messages framework can also be loaded this way. const messages = core.Messages.loadMessages('yourPluginName', 'org');

Definition of the command description (named commandDescription for clarity):

public static description = messages.getMessage('commandDescription')

Definitions of parameter descriptions (named flagnameFlagDescription for clarity):

protected static flagsConfig = { // flag with a value (-n, --name=VALUE) name: flags.string({char: 'n', description: messages.getMessage('nameFlagDescription')}), force: flags.boolean({char: 'f', description: messages.getMessage('forceFlagDescription')}) };

Here are the corresponding messages for hello:org from yourPluginName/messages/org.json.

{ "commandDescription": "print a greeting and your org IDs", "nameFlagDescription": "name to print", "forceFlagDescription": "example boolean flag", "errorNoOrgResults": "No results found for the org '%s'." }

If you prefer to write messages in a .js file instead of a .json file, enclose the message array in module.exports = { }; instead of only in { }. After you update your messages, but before testing the --help output for your commands, run:

yarn prepack

Examples and Help Text Use the examples property to teach users how to use your commands. Store your help text in the files within the yourPluginName/messages/ directory where you store your description values.

Style Guidelines for Help Text At the beginning of your examples value, include usage notes for your command. Describe the prerequisites for running the command, and warn users of any “gotchas” that they might encounter. After your usage notes, include a line of space (two newlines), the word Examples:, another line of space, and then examples of the most common usages of your command. Prefix each line that contains an example with three spaces, followed by $ sfdx. For example:

$ sfdx yournamespace:yourcommand --stringparameter "A value"

For commands in the core salesforcedx plug-ins, the examples value appears both in the -h | --help output and in the Salesforce CLI Command Reference. Don’t duplicate information in examples that’s already in your description values.

11 Salesforce CLI Plug-In Developer Guide Error Messages

How to Set Help Text In your command definition file, include an examples property (at the same level as your flagsConfig, but not within it). For that property’s value, include a pointer to your messages file. We suggest naming the element that you are pointing to commandExamples.

public static examples = messages.getMessage('commandExamples');

To include multiline messages in your messages file, use a .js or .ts file (not a .json file), and enclose your message in tick marks (`...`). This example shows the contents of a sample .js messages file for the command yournamespace:yourcommand.

module.exports = { commandDescription: 'your command description', commandLongDescription: 'A description of your command', commandExamples: `Here are some usage notes. Providing usage guidelines helps users use this command.

Here are some more usage notes.

Examples:

$ sfdx yournamespace:yourcommand --stringparameter "A value" $ sfdx yournamespace:yourcommand --stringparameter "A value" --aflag`,

stringparameterDescription: 'accept a string value', stringparameterLongDescription: 'Accepts a string value. ' + 'This value has letters and numbers in it.', aflagDescription: 'change some behavior', aflagLongDescription: 'Changes the command’s behavior when included. ' + '\nIf you don’t specify this parameter, the behavior is unchanged.', };

Note: Because the sample plug-in uses a .json file for its messages, and .json files can’t contain multiline messages, the sample plug-in stores its examples text directly in the org.ts file.

public static examples = [ `$ sfdx hello:org --targetusername [email protected] --targetdevhubusername [email protected] Hello world! This is org: MyOrg and I will be around until Tue Mar 20 2018! My hub org id is: 00Dxx000000001234 `, `$ sfdx hello:org --name myname --targetusername [email protected] Hello myname! This is org: MyOrg and I will be around until Tue Mar 20 2018! ` ];

To write messages in a .js file instead of a .json file, enclose the message array in module.exports = { }; instead of only in { }.

Error Messages Use an error message to tell users how to recover from the situation that caused the error. Store your error messages in the same files within the yourPluginName/messages/ directory where you store your description values and help text.

12 Salesforce CLI Plug-In Developer Guide Customize Your Salesforce CLI Plug-In

Style Guidelines for Error Messages Before writing an error message, find out whether the design can be changed to avoid the error. Otherwise, tell users concisely but also completely what went wrong and what they can do about it. Two short sentences are usually better than one long one. However, rather than first stating the problem and then the solution you can sometimes easily imply the problem in the solution, . When possible, say what users can do instead of what they can’t. A good error message helps users move on rather than making them feel bad.

How to Set Error Messages In the logic that detects an error scenario, use the core.SfdxError() method to add a pointer to your messages file. For example, yourPluginName/src/commands/hello/org.ts includes these lines. This SfdxError shows the org ID for the current org as part of the errorNoOrgResults message.

if (!result.records || result.records.length <= 0) { throw new core.SfdxError( messages.getMessage('errorNoOrgResults', [this.org.getOrgId()]) ); }

Here’s the corresponding message in yourPluginName/messages/org.json.

{ "commandDescription": "print a greeting and your org IDs", "nameFlagDescription": "name to print", "forceFlagDescription": "example boolean flag", "errorNoOrgResults": "No results found for the org '%s'." }

If you prefer to write messages in a .js file instead of a .json file, enclose the message array in module.exports = { }; instead of only in { }.

Customize Your Salesforce CLI Plug-In

To customize your plug-in, duplicate and update the generated files. You can customize your commands’ parameters, properties, error-handling, and output.

Salesforce CLI Command Properties Control your commands’ behavior with static and instance properties. Static properties toggle Salesforce CLI features, and instance properties give your commands access to orgs, project files, and so on. Salesforce CLI Parameters and Arguments To add custom parameters and arguments to your commands, customize the flags and args instance properties. To accept the global Salesforce CLI parameters, set static properties. We suggest that you use flags when you want the user’s input to modify a command’s behavior. When you want the user’s input to be a passed-in value, we suggest using args. Error Handling Errors thrown during the command life cycle are handled for you. If an SfdxError is not thrown, the error handler wraps the error in an SfdxError for consistent error display. SfdxCommand handles runtime errors in the catch method for consistent error-handling formatting and behavior.

13 Salesforce CLI Plug-In Developer Guide Salesforce CLI Command Properties

Display Command Results and Render Tables For complete control over rendering, use the result static property. It’s easy for your command to output results in a table format. Simple table formatting is defined with the tableColumnData static property. Hooks A hook is a piece of code that runs at a specific lifecycle event of a CLI command. Think of a hook as a pause in the CLI command execution. The command executes as usual until it encounters a hook. It pauses while the hook code executes, and then picks up again when the hook code execution is completed. Example Plug-In Customizations For ideas about how to customize your plug-ins, check out plug-ins that the Salesforce Developers community has built.

Salesforce CLI Command Properties Control your commands’ behavior with static and instance properties. Static properties toggle Salesforce CLI features, and instance properties give your commands access to orgs, project files, and so on.

Static Properties The SfdxCommand library provides static properties for Salesforce CLI features so you can easily turn them off and on. Static properties let your commands do things such as accept or require a username, require a command to run from within a Salesforce DX project, or accept key=value pairs (varargs) as parameters. Customize your command definition file to include these properties. Instance Properties Instance properties give your command convenient access to orgs (including Dev Hubs), project files, parameters, and more. Use these properties in your code to access these items.

Static Properties The SfdxCommand library provides static properties for Salesforce CLI features so you can easily turn them off and on. Static properties let your commands do things such as accept or require a username, require a command to run from within a Salesforce DX project, or accept key=value pairs (varargs) as parameters. Customize your command definition file to include these properties. These static properties are available.

Username Properties requiresUsername To add -u | --targetusername as a required parameter, and --apiversion as an optional parameter, and to add the supplied org as an instance property that your command can access as this.org, set requiresUsername to true. If the user’s Salesforce CLI config contains a default username value, the -u | --targetusername value is optional and overrides the config value.

static requiresUsername = true;

supportsUsername To add -u | --targetusername and --apiversion to a command as optional parameters, and to add the supplied org (if provided) as an instance property that your command can access as this.org, set supportsUsername to true.

static supportsUsername = true;

14 Salesforce CLI Plug-In Developer Guide Salesforce CLI Command Properties

requiresDevhubUsername To add -v | --targetdevhubusername as a required parameter, and --apiversion as an optional parameter, and to add the supplied org as an instance property that your command can access as this.hubOrg, set requiresDevhubUsername to true. If the user’s Salesforce CLI config contains a default Dev Hub username value, the -v | --targetdevhubusername value is optional and overrides the config value.

static requiresDevhubUsername = true;

supportsDevhubUsername To add -v | --targetdevhubusername and --apiversion to a command as optional parameters, and to add the supplied Dev Hub org as an instance property that your command can access as this.hubOrg, set supportsDevhubUsername to true.

static supportsDevhubUsername = true;

Other Properties deprecated To mark a command as to-be-deprecated, use the deprecated property. Set the API version to remove the command from, and the command to use instead:

protected static deprecated: { version: 49.0, to: other:command }

Alternatively, you can override the standard message with your custom message:

protected static deprecated: { messageOverride: 'We’re deprecating namespace:command --parameter in v apiversion. Instead, use namespace:command --otherparameter.' }

flagsConfig To enable, disable, or override Salesforce CLI parameters, and to configure new parameters, customize this property.

protected static flagsConfig: FlagsConfig = { // flag with a value (-n, --name=VALUE) name: flags.string({ char: 'n', description: messages.getMessage('nameFlagDescription') }), force: flags.boolean({ char: 'f', description: messages.getMessage('forceFlagDescription') }) };

requiresProject To require a command to run from within a Salesforce DX project, and to have the project object added as an instance property that your command can access as this.project, set requiresProject to true.

static requiresProject = true;

15 Salesforce CLI Plug-In Developer Guide Salesforce CLI Command Properties

result Customize this property for full control over command output formatting and display, or to override certain pieces of default display behavior. For more information about table rendering, see table.ts in the oclif/cli-ux repository on GitHub.

public static result: SfdxResult = { tableColumnData: { columns: [ { key: 'id', label: 'ID' }, { key: 'name', label: 'Name' }, { key: 'description', label: 'Description' } ] }, display() { if (Array.isArray(this.data) && this.data.length) { if (this.data.length > 100) { // special display for large number of results } else { this.ux.table(this.data, this.tableColumnData); } } } };

tableColumnData Use this string array to define table columns for simple command output table formatting.

static tableColumnData = ['id', 'name', 'description'];

varargs and VarargsConfig To enable key=value parameter input to the command, set varargs to true. To require at least one vararg, or to define a varargs validator function, use the VarargsConfig object definition. When validation fails, throw an error from the validator function with a helpful error message and action.

static varargs = true;

static varargs = { required: true, validator: (name, value) => { // Whitelist varargs parameter names if (!myWhitelist.includes(name)) { const errMsg = `Invalid parameter [${name}] found`; const errName = 'InvalidVarargName'; const errAction = `Choose one of these parameter names: ${myWhitelist.join()}`; throw new SfdxError(errMsg, errName, [errAction]); } } }

Instance Properties Instance properties give your command convenient access to orgs (including Dev Hubs), project files, parameters, and more. Use these properties in your code to access these items.

16 Salesforce CLI Plug-In Developer Guide Salesforce CLI Command Properties

args The args instance property stores the parsed arguments from the command line. If the user inputs a string that isn’t a flag after the command name, the string is treated as an arg. For example, the sfdx plugins:generate command takes an arg that’s the name of your plug-in: sfdx plugins:generate yourPluginName --defaults. For details about args, see Arguments and Varargs.

public static args = [{name: 'file'}];

if (this.flags.force && this.args.file) { this.ux.log(`You input --force and a file: ${this.args.file}`); }

flags (commonly called parameters) The flags property stores the parsed flags (parameters) from the command line. When users input flag names, long names are prefixed with two hyphens, and short names are prefixed with one hyphen. A flag can have boolean behavior, such as --setdefaultusername, or take a value, like --targetusername. In your command’s run method, this.flags looks something like:

this.flags -> { name: 'yourflag', force: true }

org The org property represents the org supplied to the command via the -u | --targetusername parameter or the user’s Salesforce CLI config.

const conn = this.org.getConnection(); const query = 'Select Name, TrialExpirationDate from Organization';

// The type we are querying for interface Organization { Name: string; TrialExpirationDate: string; }

// Query the org const result = await conn.query(query);

hubOrg The hubOrg property represents the Dev Hub org supplied to the command via the -v | --targetdevhubusername parameter or the user’s Salesforce CLI config.

if (this.hubOrg) { const hubOrgId = this.hubOrg.getOrgId(); this.ux.log(`My hub org ID is: ${hubOrgId}`); }

project The project property represents a Salesforce DX project object that represents the project in which the user is running commands. configAggregator The configAggregator property represents a ConfigAggregator object. This object aggregates global and local project config files and environment variables from sfdx-config.json.

const aggregator = await ConfigAggregator.create(); console.log(aggregator.getPropertyValue('defaultusername'));

17 Salesforce CLI Plug-In Developer Guide Salesforce CLI Parameters and Arguments

logger The logger property represents the Salesforce CLI logger. This logging abstraction is powered by Bunyan. It provides a default logger configuration, which logs to sfdx.log, and a way to create custom loggers based on the same foundation. ux The ux property represents the UX object for command output. The UX methods respect the --json flag, so the output of these methods is suppressed when --json is provided. result The result property represents the result instance where you can manipulate data and formatting after the command's run method has completed.

Salesforce CLI Parameters and Arguments To add custom parameters and arguments to your commands, customize the flags and args instance properties. To accept the global Salesforce CLI parameters, set static properties. We suggest that you use flags when you want the user’s input to modify a command’s behavior. When you want the user’s input to be a passed-in value, we suggest using args.

Global Salesforce CLI Parameters Salesforce CLI provides global parameters (flags) that your command can accept. sfdx-command enables the --json and --loglevel flags on every command to make continuous integration setup and debugging easier. Your command’s static properties control whether --targetusername and --targetdevhubusername are available. You can also implement other global parameters. Salesforce CLI Parameter Types Choose a parameter type based on the behavior you want that parameter to cause. Using a specific parameter type helps you validate the format of the parameter value that your user supplies. Sample Parameter Definitions Use the flagsConfig static property to enable, disable, or override Salesforce CLI parameters and to configure new parameters. This example illustrates a flagsConfig for a command that accepts an array (-n | --names), a boolean value (-f | --force), a JSON array (-d | --metadata), and --verbose. For this command, --names is required. Arguments and Varargs The args instance property stores the parsed arguments from the command line. If the user inputs a string that isn’t a flag after the command name, the string is treated as an arg. For example, the sfdx plugins:generate command takes an arg that’s the name of your plug-in: sfdx plugins:generate yourPluginName --defaults. Resolution Order of Parameters and Settings Salesforce CLI resolves users’ parameters, environment variables, and settings in this order. Deprecate Parameters and Arguments Here are the instructions to mark a command parameter (flag) for deprecation.

Global Salesforce CLI Parameters Salesforce CLI provides global parameters (flags) that your command can accept. sfdx-command enables the --json and --loglevel flags on every command to make continuous integration setup and debugging easier. Your command’s static properties control whether --targetusername and --targetdevhubusername are available. You can also implement other global parameters. These are the global Salesforce CLI parameters.

18 Salesforce CLI Plug-In Developer Guide Salesforce CLI Parameters and Arguments

--json Suppresses output from this.ux.* methods, and formats output as JSON. This flag is always enabled on all Salesforce CLI commands. Its value is treated as true if provided, false otherwise. --loglevel Sets the logging level for the command invocation. Logs are stored in $HOME/.sfdx/sfdx.log. This enum flag accepts only known (lowercase) logging options and is available on all Salesforce CLI commands. See the LoggerLevel docs for more information. --apiversion Overrides the default apiVersion for API requests made by the command. This parameter is available when you set the static property supportsUsername, requiresUsername, supportsDevhubUsername, or requiresDevhubUsername to true in your command definition file. Or, use the flags.builtin marker:

const flagsConfig: FlagsConfig = { apiversion: flags.builtin() };

-u | --targetusername Sets a username or alias for the target org. Overrides the default target org. This parameter is available when you set the static property supportsUsername or requiresUsername to true in your command definition file. For details about how to accept or require -u | --targetusername, see Static Properties. -v | --targetdevhubusername Sets a username or alias for the target Dev Hub org. Overrides the default Dev Hub org. This parameter is available when you set the static property supportsDevhubUsername or requiresDevhubUsername to true in your command definition file. For details about how to accept or require -v | --targetdevhubusername, see Static Properties. --concise A common flag name to use for writing brief command output to stdout. Your command is responsible for modifying output based on this flag. This flag's value is treated as true if provided, false otherwise. To enable the predefined config for this parameter, use:

const flagsConfig: FlagsConfig = { concise: flags.builtin() };

--quiet A common flag name to use for suppressing command output to stdout. Your command is responsible for modifying output based on this flag. This flag's value is treated as true if provided, false otherwise. To enable the predefined config for this parameter, use:

const flagsConfig: FlagsConfig = { quiet: flags.builtin() };

--verbose A common flag name to use for more detailed command output to stdout. Your command is responsible for modifying output based on this flag. This flag's value is treated as true if provided, false otherwise. To enable the predefined config for this parameter, use:

const flagsConfig: FlagsConfig = { verbose: flags.builtin() };

19 Salesforce CLI Plug-In Developer Guide Salesforce CLI Parameters and Arguments

Salesforce CLI Parameter Types Choose a parameter type based on the behavior you want that parameter to cause. Using a specific parameter type helps you validate the format of the parameter value that your user supplies. You can add parameters that have these Salesforce CLI parameter types. array The parameter expects an array of comma-separated values or separated by a delimiter that you define. You can also define a custom array-element-mapping function with the signature (val: string) => T, which converts a string array element into a custom type T. Sample user input:

--yourarrayflag first,second,third

or:

--yourarrayflag "first name, last name, suffix"

or:

--yourpathsarrayflag=/tmp/a.txt:/tmp/b.txt

Sample flagsConfig property:

yourarrayflag: flags.array({ description: 'your description' })

Sample flagsConfig property for a parameter that accepts array of integers:

yourintarrayflag: flags.array({ description: 'your description', map: v => parseInt(v, 10) })

Sample flagsConfig property for a parameter that accepts array of colon-delimited paths:

yourpathsarrayflag: flags.array({ description: 'your description', delimiter: ':', map: (val: string) => require('path').parse(val) })

The same sample flagsConfig property, with a more succinct definition of map:

yourpathsarrayflag: flags.array({ description: 'your description', delimiter: ':', map: require('path').parse })

In your run method, this.flags.yourpathsarrayflag would have the value:

[ { root: '/', dir: '/tmp', base: 'a.txt', ext: '.txt', name: 'file' }, { root: '/', dir: '/tmp', base: 'b.txt', ext: '.txt', name: 'file' } ]

boolean The parameter doesn’t accept a value. Its value is true if the parameter is supplied, or false otherwise.

20 Salesforce CLI Plug-In Developer Guide Salesforce CLI Parameters and Arguments

Sample user input:

--yourbooleanflag

Sample flagsConfig property:

yourbooleanflag: flags.boolean({ description: 'your description' })

In your run method, this.flags.yourbooleanflag would have the value:

true

date The parameter expects a date. Sample user input:

--yourdateflag 01-02-2000

or:

--yourdateflag "01-02-2000 GMT"

Sample flagsConfig property:

yourdateflag: flags.date({ description: 'your description' })

A date input produces a JavaScript Date object containing an absolute ISO date value. In your run method, the value of this.flags.yourdateflag depends on the local system time zone. The Date object for an input of 01/02/2000 GMT would have the value:

'2000-01-02T00:00:00.000Z'

An input of 01/02/2000 (without a specified time zone) by a user whose system is set to Pacific Standard Time (PST) would produce a Date object with the value:

'2000-01-02T00:00:00.000-08:00'

datetime The parameter expects a date and time. Sample user input:

--yourdatetimeflag "01/02/2000 01:02:34"

or:

--yourdatetimeflag "01/02/2000 01:02:34 GMT"

Sample flagsConfig property:

yourdatetimeflag: flags.datetime({ description: 'your description' })

A datetime input produces a JavaScript Date object containing an absolute ISO date value. In your run method, the value of this.flags.yourdatetimeflag depends on the local system time zone. The Date object for an input of 01/02/2000 01:02:34 GMT would have the value:

'2000-01-02T01:02:34.000Z'

21 Salesforce CLI Plug-In Developer Guide Salesforce CLI Parameters and Arguments

An input of 01/02/2000 01:02:34 (without a specified time zone) by a user whose system is set to Pacific Standard Time (PST) would produce a Date object with the value:

'2000-01-02T01:02:34.000-08:00'

directory The parameter expects a path to a directory. Sample user input:

--yourdirectoryflag /your/path/to

Sample flagsConfig property:

yourdirectoryflag: flags.directory({ description: 'your description' })

In your run method, this.flags.yourdirectoryflag would have the value:

'/your/path/to'

email The parameter expects an email address. Sample user input:

--youremailflag [email protected]

Sample flagsConfig property:

youremailflag: flags.email({ description: 'your description' })

In your run method, this.flags.youremail would have the value:

'[email protected]'

enum The parameter expects a string value that’s included in a predefined enumeration of options. Sample user input:

--yournenumflag PredefinedOption1

Sample flagsConfig property:

yourenumflag: flags.enum({ description: 'your description', options: ['PredefinedOption1', 'PredefinedOption2'] })

In your run method, this.flags.yourenumflag would have the value:

'PredefinedOption1'

filepath The parameter expects a path to a file. Sample user input:

--yourfilepathflag /your/path/to/yourfile.json

22 Salesforce CLI Plug-In Developer Guide Salesforce CLI Parameters and Arguments

Sample flagsConfig property:

yourfilepathflag: flags.filepath({ description: 'your description' })

In your run method, this.flags.yourfilepathflag would have the value:

'/your/path/to/yourfile.json'

help Augments the built-in --help output with an optional custom char value, such as h. This boolean flag causes the CLI to emit help information instead of running your command. Sample user input:

--help

Sample flagsConfig property:

help: flags.help({ char: 'h' })

If your user includes a help flag, your command’s run method is not invoked. id The parameter expects a Salesforce ID. It produces a string value if the user’s input can be validated with @salesforce/core#sfdc.validateSalesforceId. Sample user input:

--youridflag 00Dxxxxxxxxxxxx

Sample flagsConfig property:

youridflag: flags.id({ description: your description' })

In your run method, this.flags.youridflag would have the value:

'00Dxxxxxxxxxxxx'

integer The parameter expects an integer value. Use the optional min and max properties to constrain the range of valid inputs. Sample user input:

--yourintegerflag 42

Sample flagsConfig property:

yourintegerflag: flags.integer({ description: 'your description', min: -123, max: 8765309 })

In your run method, this.flags.yourintegerflag would have the value:

42

23 Salesforce CLI Plug-In Developer Guide Salesforce CLI Parameters and Arguments

milliseconds The parameter expects an integer value to be interpreted as a number of milliseconds. If the user’s input can be validated as an integer number, this flag produces a @salesforce/kit#Duration. Use the optional min and max properties to constrain the range of valid inputs. Sample user input:

--yourmillisecondsflag 42000

Sample flagsConfig property:

yourmillisecondsflag: flags.milliseconds({ description: 'your description', min: 0, max: 86400000 })

In your run method, this.flags.yourmillisecondsflag would have the value:

Duration.milliseconds(42000)

minutes The parameter expects an integer value to be interpreted as a number of minutes. If the user’s input can be validated as an integer number, this flag produces a @salesforce/kit#Duration. Use the optional min and max properties to constrain the range of valid inputs. Sample user input:

--yourminutesflag 1

Sample flagsConfig property:

yourminutesflag: flags.minutes({ description: 'your description', min: 1, max: 1440 })

In your run method, this.flags.yourminutesflag would have the value:

Duration.minutes(1)

number The parameter expects a number. Use the optional min and max properties to constrain the range of valid inputs. Sample user input:

--yournumberflag 42

or:

--yournumberflag -12345.67

Users can also supply values in binary, octal, or hex formats by prefixing those values with 0b, 0o, or 0x, respectively. For example, to supply a binary number whose value in base 10 is 5:

--yournumberflag 0b101

24 Salesforce CLI Plug-In Developer Guide Salesforce CLI Parameters and Arguments

Sample flagsConfig property:

yournumberflag: flags.number({ description: 'your description', min: -1.23, max: 8765309 })

In your run method, this.flags.yournumberflag would have the value:

42

or:

-12345.67

or:

5

option The parameter expects a string value to be parsed by a custom parse function. Sample user input:

--youroptionflag "stRing VALue to pArsE"

Sample flagsConfig property:

youroptionflag: flags.option({ description: 'your description', parse: (val: string) => val.toUpperCase() })

In your run method, this.flags.youroptionflag would have the value:

'STRING VALUE TO PARSE'

seconds The parameter expects an integer value to be interpreted as a number of seconds. If the user’s input can be validated as an integer number, this flag produces a @salesforce/kit#Duration. Use the optional min and max properties to constrain the range of valid inputs. Sample user input:

--yoursecondsflag 42

Sample flagsConfig property:

yoursecondsflag: flags.seconds({ description: 'your description', min: 0, max: 86400 })

In your run method, this.flags.yoursecondsflag would have the value:

Duration.seconds(42)

25 Salesforce CLI Plug-In Developer Guide Salesforce CLI Parameters and Arguments

string The parameter expects a string value that can be up to 2^53 characters long. Sample user input:

--yourstringflag "your awesome string value"

Sample flagsConfig property:

yourstringflag: flags.string({ description: 'your description' })

In your run method, this.flags.yourstringflag would have the value:

'your awesome string value'

url The parameter expects a URL. Sample user input:

--yoururlflag https://developer.salesforce.com/docs

Sample flagsConfig property:

yoururlflag: flags.url({ description: 'yourdescription' })

In your run method, this.flags.yoururlflag would have the value:

'https://developer.salesforce.com/docs'

In your run method, the value of this.flags.yoururlflag would be a URL object that contains the parsed URL string 'https://developer.salesforce.com/docs'. For information about the URL object, see URL in the Node.js documentation. version A boolean flag that causes the CLI to emit version information instead of running your command. Sample user input:

--version

Sample flagsConfig property:

version: flags.version()

If your user includes a version flag, your command’s run method is not invoked.

Sample Parameter Definitions Use the flagsConfig static property to enable, disable, or override Salesforce CLI parameters and to configure new parameters. This example illustrates a flagsConfig for a command that accepts an array (-n | --names), a boolean value (-f | --force), a JSON array (-d | --metadata), and --verbose. For this command, --names is required.

Example:

protected static flagsConfig: FlagsConfig = { // use a Salesforce CLI parameter type names: flags.array({ char: 'n',

26 Salesforce CLI Plug-In Developer Guide Salesforce CLI Parameters and Arguments

required: true, description: 'names to update' }),

// use an oclif flag builder force: flags.boolean({ char: 'f', description: 'force the name updates' }),

// create a custom flag with its own parser metadata: flags.option({ char: 'd', description: 'a JSON array of metadata', parser: JSON.parse }),

// enable the built-in Salesforce CLI flag --verbose verbose: flags.builtin() };

Arguments and Varargs The args instance property stores the parsed arguments from the command line. If the user inputs a string that isn’t a flag after the command name, the string is treated as an arg. For example, the sfdx plugins:generate command takes an arg that’s the name of your plug-in: sfdx plugins:generate yourPluginName --defaults. User-supplied strings can be traditional args, which users supply only as values, or varargs-style args, which users supply in name=value format. For example, users would enter a value for a traditional arg as sfdx your:command val1 and a vararg value as sfdx your:command arg1=val1. If you use traditional args, the first non-flag string that a user inputs is treated as the first arg, the second is treated as the second arg, and so on. For example, if your:command has two args, arg1 and arg2, and the user inputs sfdx your:command val2, the value of arg1 is val2, and arg2 is undefined. You can enter varargs in any order. When you set the varargs static property to true, all args for your command must be varargs.

Resolution Order of Parameters and Settings Salesforce CLI resolves users’ parameters, environment variables, and settings in this order. 1. Parameters and arguments supplied on the command line override all other values. 2. The contents of command-line-given configuration files, such as scratch org definition files or user-supplied file paths, override everything except command-line parameters and arguments. 3. Environment variables, such as SFDX_LOG_LEVEL, override everything except values and files supplied on the command line. 4. Workplace settings, such as the values in yourWorkspaceRoot/.sfdx/sfdx-config.json, are honored in the absence of conflicting environment variables or values supplied on the command line. 5. Global settings, such as the values in $HOME/.sfdx/sfdx-config.json, are honored in the absence of conflicting values in other categories in this list.

27 Salesforce CLI Plug-In Developer Guide Error Handling

Deprecate Parameters and Arguments Here are the instructions to mark a command parameter (flag) for deprecation. You can use either of these methods with the deprecatedproperty. • Set the API version to remove the parameter from and the parameter to use instead:

protected static deprecated: { version: 49.0, to: other:parameter }

• Alternatively, you can override the standard message with your custom message:

protected static deprecated: { messageOverride: 'We’re deprecating namespace:command --parameter in v apiversion. Instead, use namespace:command --otherparameter.' }

Error Handling Errors thrown during the command life cycle are handled for you. If an SfdxError is not thrown, the error handler wraps the error in an SfdxError for consistent error display. SfdxCommand handles runtime errors in the catch method for consistent error-handling formatting and behavior. By default, the exit code for errors is 1. To override this behavior, specify a different exit code in SfdxError. Stack traces are suppressed unless the SFDX_ENV environment variable's value is set to development. Override the catch method if you prefer to handle errors within your command.

Example:

// To load a message bundle: Messages.importMessagesDirectory(__dirname); this.messages = Messages.loadMessages('myPackageName', 'myBundleName'); // Note that __dirname should contain a messages folder.

// To throw an error associated with the message from the bundle: throw SfdxError.create('myPackageName', 'myBundleName', 'MyErrorMessageKey', [messageToken1]);

// To throw a non-bundle-based error: throw new SfdxError(myErrMsg, 'MyErrorName');

To simplify testing, we recommend that you name your errors. Using descriptive error names lets you look for the names in unit test results, which isn’t possible when you use traditional numeric error codes.

if (!result.records || result.records.length <= 0) { throw new core.SfdxError(messages.getMessage('errorNoOrgResults', [this.org.getOrgId()]), 'MyAwesomeError'); }

28 Salesforce CLI Plug-In Developer Guide Display Command Results and Render Tables

Display Command Results and Render Tables For complete control over rendering, use the result static property. It’s easy for your command to output results in a table format. Simple table formatting is defined with the tableColumnData static property. For information about tableColumnData and result, see Static Properties.

Hooks A hook is a piece of code that runs at a specific lifecycle event of a CLI command. Think of a hook as a pause in the CLI command execution. The command executes as usual until it encounters a hook. It pauses while the hook code executes, and then picks up again when the hook code execution is completed. For example, a CLI command has the Open CLI Framework (oclif) hook init defined. When you run the command, the init hook fires after the CLI is initialized but before the CLI actually runs the command. Salesforce supports all the oclif hooks. And you get a set of Salesforce CLI-specific hooks to customize the behavior of some of the Salesforce CLI commands. For example, you can use a hook for force:source:push to make IDs in source files unique right after they’re converted to metadata format but before they’re sent to the Salesforce org. Create a hook by adding TypeScript or JavaScript code and configuration information to a custom Salesforce CLI plug-in. When a user installs the custom plug-in into their CLI, the hook fires when the user runs the associated CLI command. The hook continues to fire for the associated Salesforce CLI command until the user explicitly uninstalls the custom plug-in. If you want the hooks to fire in a CI/CD job, install the custom plug-in before you run any of the associated Salesforce CLI commands.

Create a Salesforce CLI Hook Create Salesforce CLI hooks just like you create oclif hooks. Salesforce CLI Hook Types Use these Salesforce CLI hooks to customize your plug-in.

SEE ALSO: oclif Hooks

Create a Salesforce CLI Hook Create Salesforce CLI hooks just like you create oclif hooks. See this Github repo for the complete example referenced here. 1. Code the hook in TypeScript or JavaScript. See the sample repo’s README for a description of some of the code elements. 2. Add the TypeScript or JavaScript source file to your Salesforce project directory. 3. Update your custom plug-in package.json file and add a hooks object inside the oclif object. The hooks object specifies the type of Salesforce CLI hook, such as predeploy, and the location of your compiled source. For example:

{ "name": "my-hooks-plugin", "version": "1.0.0", "description": "Plug-in that contains custom hooks.", "oclif": { "commands": "./lib/commands", "bin": "sfdx",

29 Salesforce CLI Plug-In Developer Guide Hooks

"hooks": { "predeploy": "./lib/hooks/predeploy/metadataReplaceDeploy" } … }

See Salesforce CLI Hook Types on page 30 for the full list of hook types.

4. After you code and build your custom plug-in, users install it in their CLI as usual.

$ sfdx plugins:install my-hooks-plugin

From this point on, or until the user uninstalls the plug-in, the hook fires when the user runs the associated Salesforce CLI command. For the predeploy hook, for example, the commands that fire it are force:source:push and force:source:deploy.

Salesforce CLI Hook Types Use these Salesforce CLI hooks to customize your plug-in. Most of the property names of the Salesforce CLI hook result types describe themselves, such as PostOrgCreateResult.expirationDate and PreRetrieveResult.packageXmlPath. But a quick word about two of these properties: • aggregateName refers to a single representation in metadata format of, for example, a custom object. • workspaceElements is an array of source format files for the same custom object, each file describing the associated fields, layouts, and so on. predeploy • Description: Fires after the CLI converts your source files to Metadata API format but before it sends the files to the org. • Commands that fire this hook: force:source:push, force:source:deploy • Sample use case: Make systematic changes to the MD API files before they’re sent to the org, such as updating IDs to make them unique so the deploy doesn’t fail. • Result type:

type PreDeployResult = { [aggregateName: string]: { mdapiFilePath: string; workspaceElements: { fullName: string; metadataName: string; sourcePath: string; state: string; deleteSupported: boolean; }[]; }; }

postdeploy • Description: Fires after the CLI sends the metadata to the org and the org sends back a confirmation. • Commands that fire this hook: force:source:deploy, force:source:push • Sample use case: Post a notification or display a custom error message when metadata types fail to deploy in the org.

30 Salesforce CLI Plug-In Developer Guide Hooks

• Result type: This hook returns the full DeployResult Metadata API result object.

type PostDeployResult = { // See the DeployResult Metadata API result object }

preretrieve • Description: Fires before the CLI sends a request to the org to fetch files. • Commands that fire this hook: force:source:retrieve, force:source:pull • Sample use case: Retrieve additional metadata or configuration when a specific source file is retrieved. • Result type:

type PreRetrieveResult = { packageXmlPath: string }

postretrieve • Description: Fires right after the CLI retrieves the metadata from the org. The metadata isn’t converted to source format yet. • Commands that fire this hook: force:source:retrieve, force:source:pull • Sample use case: Modify the retrieved metadata files before they’re converted to source format. • Result type:

type PostRetrieveResult = { [aggregateName: string]: { mdapiFilePath: string; } }

postsourceupdate • Description: Fires after the CLI converts the files it fetched from the org to source format. • Commands that fire this hook: force:source:pull, force:source:retrieve • Sample use case: Modify the retrieve source files. • Result type:

type PostSourceUpdateResult = { [aggregateName: string]: { workspaceElements: { fullName: string; metadataName: string; sourcePath: string; state: string; deleteSupported: boolean; }[]; }; }

postorgcreate • Description: Fires immediately after a new scratch org or sandbox is created. You can access the org from the hook. • Commands that fire this hook: force:org:create

31 Salesforce CLI Plug-In Developer Guide Example Plug-In Customizations

• Sample use case: Set up a cron job to send a notification one day before the org expires. • Result type:

type PostOrgCreateResult = { accessToken: string, clientId: string, created: string, createdOrgInstance: string, devHubUsername: string, expirationDate: string, instanceUrl: string, loginUrl: string, orgId: string, username: string }

Example Plug-In Customizations For ideas about how to customize your plug-ins, check out plug-ins that the Salesforce Developers community has built. Wade Wegner, the Salesforce DX senior vice president for Product Management, wrote a plug-in that makes his job easier. He added commands to work with package directories, connected apps, trace flags, and more. You can peruse his code at https://github.com/wadewegner/sfdx-waw-plugin. Shane McLaughlin, Salesforce’s director of Technical Product Marketing, wrote a whole slew of commands. The code for this wealth of functionality lives at https://github.com/mshanemc/shane-sfdx-plugins. René Winkelmeyer, a principal developer evangelist at Salesforce, also wrote a plug-in. It updates your project’s metadata to use a new API version, removes unused aura files, and generates Apex classes from Swagger or OpenAPI files. Its source lives at https://github.com/muenzpraeger/sfdx-plugin. At Dreamforce ’18, the session Build and Release a CLI Plug-In - LIVE! gave a detailed overview of plug-in development. Watch the video of the presentation, and explore the source for the streamer plug-in that’s featured in the session. To hear about other plug-ins that the community has developed, or to share your plug-in with the world, join the Salesforce DX group in the Trailblazer Community. To look for other Salesforce CLI plug-ins to play with, search npmjs.com for sfdx. Because the package.json file for each Salesforce CLI plug-in contains an sfdx plug-in tag, you can find all published CLI plug-ins this way.

Test Your Salesforce CLI Plug-In

While you’re coding and customizing your plug-in, be sure to write associated tests for each new feature to ensure that it’s working as you expect. In your test suites, include both unit tests and more complex integration, smoke, and end-to-end tests. To help you with latter, we’ve created a library of testing utilities that you add to your environment as a Yarn developer dependency. To help you get started, we provide descriptions of common use cases and associated sample testing code. And we link to other public GitHub repos that use the library so you can see the testing utilities in action. Check out all the code and documentation in the cli-plugins-testkit GitHub repo.

32 Salesforce CLI Plug-In Developer Guide Debug Your Salesforce CLI Plug-In

Debug Your Salesforce CLI Plug-In

We recommend using the Visual Studio Code (VS Code) editor, with Salesforce Extensions for VS Code, for your plug-in development. Included in the .vscode directory of Salesforce Plug-In Generator’s generated plug-ins is a launch.json config file. This config file allows you to attach a debugger to the Node process when running your commands. If you don’t already use Salesforce Extensions for VS Code, install it from the Visual Studio Marketplace. Follow these steps to debug the hello:org command in the yourPluginName directory. 1. Start the inspector. • If you’ve linked your plug-in to Salesforce CLI (by running sfdx plugins:link), run your command with the --dev-suspend flag.

sfdx hello:org -u [email protected] --dev-suspend

• Or, to debug your plug-in before linking it to Salesforce CLI, call your command using the bin/run script. Set the NODE_OPTIONS environment variable to --inspect-brk when starting the debugger.

NODE_OPTIONS=--inspect-brk bin/run hello:org -u [email protected]

2. Set some breakpoints in your command code. To set a breakpoint, click in the gutter to the left of a line number in VS Code’s editing view. 3. To open up Debug view, click the Debug icon in the activity bar on the left side of the VS Code window. 4. In the dropdown list in the upper-left corner of the Debug view, verify that the Attach to Remote launch configuration is selected. 5. Click the green play button to the left of the launch configuration dropdown list. The debugger stops on the first line of the program. 6. Click the green play button in the top-middle of the VS Code window. The debugger stops on your first breakpoint. Congratulations, you are now debugging! For details about debugging your code in VS Code, see Debugging in the Visual Studio Code docs.

Tip: Setting environment variables also helps with debugging. To get stack traces in your error messages when your commands fail, set SFDX_ENV=development and NODE_ENV=development. To see trace-level logging information in the console, set DEBUG=*. Setting DEBUG=* has the same effect as running your command with the --dev-suspend flag.

Best Practices for Salesforce CLI Plug-In Development

We suggest that you follow these patterns when developing plug-ins for Salesforce CLI.

Synchronous Behavior For commands that wait for a response from the server, reassure the user that the command is churning. If the command times out, provide next steps. Asynchronous Behavior If your command uses asynchronous API functionality, make the command return a job ID so that users can check the status and don’t wait, unless the user supplies a --wait value. Default Values Use the default property to assign default values for your parameters. To set possible values, use the options property. Add these properties to your flagsConfig object, along with char, description, and so on.

33 Salesforce CLI Plug-In Developer Guide Synchronous Behavior

Synchronous Behavior For commands that wait for a response from the server, reassure the user that the command is churning. If the command times out, provide next steps. While waiting for a server response, emit Processing... to stdout at regular intervals. For example, show this output when the command is first run and then every 30 seconds until the command finishes. Use the spinner to show that the command is still running, but provide an option to disable the spinner for automatic invocations. To control the spinner, call one of the ux.*Spinner* methods (with this.ux.getSpinnerStatus, this.ux.setSpinnerStatus, this.ux.startSpinner, this.ux.pauseSpinner, or this.ux.stopSpinner). If your user includes the --json flag when running your command, suppress the spinner and the Processing... feedback. If your user includes --verbose, provide more detailed information, such as the amount of time that has passed since the command’s invocation. When your command times out, provide specific information about what to do next. If you want the user to run another command, show the full command (including sfdx, all the relevant parameters, and the appropriate values) in double quotes.

Asynchronous Behavior If your command uses asynchronous API functionality, make the command return a job ID so that users can check the status and don’t wait, unless the user supplies a --wait value. In your asynchronous command’s output, include the command that users can run to check the job’s status. Provide the full command, including sfdx, all relevant parameters, and the appropriate values, in double quotes. Write supporting commands for users to check your asynchronous command’s status. Use *:status commands for basic job status information, and provide *:report commands to retrieve and report on results. An example of a supporting command in the salesforcedx aggregate plug-in is force:apex:test:report, which supports force:apex:test:run. If a user enters a --wait value (in minutes), allow your command to wait for results for the specified amount of time. While the command waits, set up polling. Use the streaming client or, if streaming isn’t available for your entity, the polling client. For details, see StreamingClient and PollingClient in the @salesforce/core documentation. During the time while the command is waiting, treat the command as if it were synchronous. For details, see Synchronous Behavior.

Default Values Use the default property to assign default values for your parameters. To set possible values, use the options property. Add these properties to your flagsConfig object, along with char, description, and so on. This sample flagsConfig includes an email parameter that has a custom type, potential values, and a default value.

Example:

protected static flagsConfig: FlagsConfig = { name: flags.string({ char: 'n', description: messages.getMessage('nameFlagDescription') }), force: flags.boolean({ char: 'f', description: messages.getMessage('forceFlagDescription') }), email: flags.email({

34 Salesforce CLI Plug-In Developer Guide Resources for Salesforce CLI Plug-In Development

char: 'e', description: messages.getMessage('emailFlagDescription'), options: ['[email protected]', '[email protected]', '[email protected]'], default: '[email protected]' }) };

Resources for Salesforce CLI Plug-In Development

Bookmark these resources so that you can refer to them as you develop plug-ins. • Contribute to Salesforce Plug-In Generator, the @salesforce/core library, and the @salesforce/command library on GitHub. • Read about changes to Salesforce Plug-In Generator in its change log. • Find the SfdxCommand class in the @salesforce/command package on npm. • Peruse reference documentation for the Salesforce DX Core Library. • Watch a video of the Dreamforce ’18 session Build and Release a CLI Plug-In - LIVE!, and explore the source for the streamer plug-in that’s featured in the session. This plug-in listens to platform and streaming events. • For information about the CLI framework, check out the oclif repositories. – You can use the base command for oclif, @oclif/command, with or without Salesforce Plug-In Generator. – Most of the core setup for oclif lives in @oclif/config. – @oclif/cli-ux is oclif’s library for common CLI UI utilities. – @oclif/test is the test helper for oclif.

• Become familiar with the documentation for the Salesforce DX toolset. – To learn how to install and set up Salesforce CLI, check out the Salesforce CLI Setup Guide. – To learn about enabling Dev Hub and Second-Generation Packaging (2GP), Salesforce DX projects, and working with Salesforce CLI, scratch orgs, packaging, and continuous integration systems, check out the Salesforce DX Developer Guide. – To learn about the commands in the core salesforcedx plug-ins, check out the Salesforce CLI Command Reference. – To learn about Salesforce Extensions for VS Code (the Salesforce extension pack for the Visual Studio Code editor), check out the extension pack’s docs on the Visual Studio Marketplace.

• To learn about the Salesforce DX toolset in a fun, guided format, check out the Get Started with Salesforce DX trail on Trailhead. • For tips, tricks, and advice on working with the Salesforce DX tools (including Salesforce Plug-In Generator!), and to share your plug-ins with the world, join the Salesforce DX group in the Trailblazer Community.

35