Create Your First Salesforce CLI Plugin

In our recent blog series Getting Started with Salesforce DX, we introduced you to the capabilities of the Salesforce CLI. This post introduces you to the overall architecture of Salesforce CLI plugins, how to get started with the new plugin generator, and the new Salesforce DX libraries as well as some considerations for your first plugin project.

The Salesforce CLI architecture

Before we start it’s important to learn what the moving parts around the Salesforce CLI are, and how they work together:

 

Salesforce CLI
The CLI represents the core sfdx-cli. It is what you execute on your local machine or on your CI/CD system. It provides functionality to install custom plugins. By the way, did you know that the CLI doesn’t “know” much about Salesforce DX? Next, you’ll see why.

salesforcedx
The CLI itself doesn’t know much about Salesforce DX. That is because Salesforce DX functionality is provided by the salesforcedx plugin. It is bundled with the Salesforce CLI, which is why it’s not obvious in the first place that it’s a plugin. You check the installed version by running the command sfdx plugins on your machine.

@salesforce/plugin-generator
Here it becomes interesting. The plugin generator, available as a scoped npm package on npmjs, is also a plugin by itself. NPM package scopes are, if you’re not familiar with them, like namespaces. Like the salesforcedx plugin it’s bundled with the CLI installation. You check the installed version by running the command sfdx plugins —core. With the generator you set up a local Salesforce CLI plugin development project. The new plugin generator is based on Heroku’s open-source CLI framework oclif. This framework enables developers to create their own standalone CLI applications, as well as CLI plugins. All plugins generated with the sfdx plugin generator are based on oclif. We also leveraged oclif to generate the plugin generate itself.

Generating your first plugin

The Salesforce CLI has supported custom plugins from the start. This support wasn’t backed by a solid framework though — you had to build your own nodejs project for that. You also had no out-of-the-box access to required information, like the default DevHub, config parameters for the current Salesforce DX project, and more. All these gaps are now closed with the new plugin framework and some new Salesforce DX libraries. I’ll talk more about the libraries later. For now, let’s focus on the generator.

By executing the Salesforce CLI command sfdx plugins:generate, the plugin wizard leads you through the configuration steps for the new project and generates the plugin project in the current directory:


Noteworthy:

  • As the generated plugin project is based on oclif you’ll see that it’s very similar to the oclif example TypeScript plugin.
  • Having some basic knowledge around using yarn as a dependency manager as well as coding experience in TypeScript (Based on ES8 / ECMAScript 2017) is recommended. If you’re not familiar with one or the other, time to jump on it!
  • The generated README contains sample documentation as well as instructions on how to debug your plugin.
  • The generator creates sample configurations for different CI systems (Like CircleCI or Appveyor CI) as well as tests in the test folder. You won’t need any of this when starting.

Once the project is generated you run the command sfdx plugins:link directory (Where directory represents the project folder). This links the local project to the Salesforce CLI, and adds the topic hello:org. If you’re not familiar with CLI topics you should read the “Topical namespace” section of this blog post (Well, you should read the post in general).

Here’s the output when executing the example command hello:org from our freshly linked plugin:

➜ sfdx-demo-plugin git:(master) ✗ sfdx hello:org -u einstein
Hello world! This is org: einstein-ai and I will be around until Fri Jun 08 2018!
My hub org id is: 00D1I000001frNvUAI

By passing the -u parameter with the alias of one of my scratch orgs, the example plugin connects to the org, reads some metadata and prints it to the console.

An alternative to link the plugin is to execute ./bin/run topic:command --flag flagvalue. This is basically a standalone call of your plugin, outside of the Salesforce CLI, which is good for initial development.

Using the new Salesforce DX libraries

When you look at the hello:org command you’ll see that it’s not too difficult to navigate the plugin code and understand how it works. At the top of the file, you’ll notice the import of SfdxCommand and core from @salesforce/command.

import { flags } from '@oclif/command';
import { SfdxCommand, core } from '@salesforce/command';

These are the new Salesforce DX libraries that I mentioned earlier. Both are available on npmjs (@salesforce/command / @salesforce/core).

The SfdxCommand library gives you access to the context of the command itself. That context can include flags that you pass as parameters to the CLI, instances of the current Salesforce DX project, or rendering command output as table. Check out the npmjs package site for examples and detailed documentation.

When you look at the documentation for @salesforce/core, you’ll see a wealth of options to interact with your global Salesforce DX configuration, projects, orgs, and the user interface of the CLI.

With these libraries, you can do lots of cool things. Did you know that that the CLI wraps the JSForce library internally? Yes, it does. With that you have access to all methods and API calls that JSForce supports. That can be the Metadata API, the Tooling API, or even the Analytics API as in the next code snippet:

const conn = this.org.getConnection();
// see here: https://jsforce.github.io/document/#analytics-api
const reports = conn.analytics.reports;

Easy, right? If you want to read the package directory configuration from your sfdx-project.json, no problem — resolveProjectConfig() is your friend.

const projectJson = await project.resolveProjectConfig();
const packageDirectories: any[] = projectJson['packageDirectories']; // tslint:disable-line:no-any
for (const packageConfig of packageDirectories) {
   // do stuff with the array elements
}

On May 10th, we also released a new plugins section to the sfdx-project.json (Check out the CLI Release Notes here). If you have long running operations you can use some of the spinner methods to show a nice spinner beside the message on the terminal (My colleague Kevin loves the spinner…).

this.ux.startSpinner('Hey ho, the spinner starts');
await theSuperLongRunningOperation();
this.ux.stopSpinner('Yay, it finished');

There are many more things you can do, so make sure to dive into the API documentation and explore the many options around Org, Aliases, SfdxUtil and more.

A few things to consider

The plugin generator is currently in beta, so do expect some changes along the way. You cannot use any random JavaScript library within your projects, as the defined TypeScript rules are quite strict (And that’s a great thing). It may be that you’ll have to search for some time to find an appropriate library in TypeScript – or you just write your own TypeScript declaration file. Check out the scripts section of the projects package.json and make yourself familiar with them. For example, check out the yarn prepare command here, which automatically updates the README file of the project. If it seems like your code changes are not having any effect, also run yarn prepare and the changes should show up.

Summary

For an automation junkie like me, there’s nothing better than this easy-to-use framework for the Salesforce CLI. I highly encourage you to start building your first plugin today. If you already have custom scripts, like the ones shown in this blog post, go and revisit if it makes sense to re-build them as CLI plugins. Whenever you want to perform repeating tasks across/within different sfdx repos or you have to support multiple operating systems for your developers, plugins are the way to go. I did that already with a few of my scripts and it makes my development workflow so much easier.

A few resources that you should check out:

About the author

René Winkelmeyer works as Principal Developer Evangelist at Salesforce. He focuses on enterprise integrations, mobile, and security with the Lightning Platform. You can follow him on Twitter @muenzpraeger.

Published
May 17, 2018

Leave your comments...

Create Your First Salesforce CLI Plugin