-
Notifications
You must be signed in to change notification settings - Fork 25
Migrate Plugins Built for sfdx
You can install and use plugins originally built for sfdx
in sf
, although the general look-and-feel is different. We therefore recommend that you explicitly migrate your sfdx
plugin to sf
so its commands look and behave the same as other sf
commands. You can also then take advantage of the many improvements we've made to the code that supports sf
plugins.
If you haven't already, update @oclif
and the @salesforce/core
library to their current versions.
- Update to v3 of
@oclif/core
by following this document. - Update to v7 of
@salesforce/core
by following this document. There are documents detailing every major change, and upgrade tips at the root of the repository, which might be helpful if you're upgrading from an old version.
Salesforce provides a set of eslint rules for Salesforce CLI plugins.
When migrating an sfdx
plugin to sf
, we recommend you use a set of additional rules that do most of the work for you. See the setup instructions for more details.
Be aware that some of these eslint rules automatically change some of your code while other rules provide suggestions where you can choose which fix to apply.
Although v3+ of @salesforce/core
works with your existing JSON message files, we recommend that you migrate them to Markdown. Message files contain the --help
content and error messages.
-
If you haven't already, install
plugin-dev
:sf plugins install dev
-
From the top-level directory of your plugin, run the
dev convert messages
command to convert an existing JSON file to a first-draft Markdown file to get you started. For example:sf dev convert messages --file-name messages/mycommand.json
In this case, the command generates the Markdown file
messages/mycommand.md
. -
Edit the resulting Markdown file to ensure your messages look good. See the next section for a tip to reduce maintenance, and then Write Useful Messages for general information about writing messages.
-
Run
sf dev audit messages
to find missing or unused messages, then update your code to add or remove references to the messages as needed. -
Remember to delete the old JSON files when you're finished!
Messages in v3+ of @salesforce/core
support dynamic references to the command and Salesforce CLI binary names. Specifically, the placeholder <%= config.bin %>
refers to the binary name (sfdx
or sf
) and <%= command.id %>
refers to the command.
If your plugin supports both sfdx
and sf
, use these placeholders in message files rather than hardcoding the command and CLI executable name. This way you won't need to rewrite the messages when you change your command structure or name.
Here's an example from the Markdown message file for the org delete sandbox
command:
# examples
- Delete a sandbox with alias my-sandbox:
<%= config.bin %> <%= command.id %> --target-org=my-sandbox
We encourage you to follow sf
's styles in your command and flag names; see Design Your Plugin for details. If you need to maintain backward compatibility, you have some options.
If you rename a command, you can alias it back to its original name.
export class LimitsApiDisplayCommand extends SfCommand<ApiLimits> {
...
// this command is now `limits api display` but was previously 'force:limits:api:display'` so an alias keeps that old name operable
public static readonly aliases = ['force:limits:api:display', 'org:list:limits'];
// when someone uses the old name, display a warning encouraging them to use the new name.
// without this property, the old name works but no warning occurs.
public static deprecateAliases = true;
Similarly, if you rename a flag, you can alias it back to its previous name.
sobject: Flags.string({
char: 's',
required: true,
summary: messages.getMessage('flags.sobject'),
// the flag's previous name could be simplified to a single word
aliases: ['sobjecttype'],
// display a warning when someone uses the previous flag name
deprecateAliases: true,
}),
// sf uses the more readable hyphenated name
'use-tooling-api': Flags.boolean({
char: 't',
summary: messages.getMessage('flags.useToolingApi'),
aliases: ['usetoolingapi'],
deprecateAliases: true,
}),
// this flag was renamed and its short character was changed. both -f and -p work now, but -p provides a warning when used
'file': Flags.boolean({
char: 'f',
summary: messages.getMessage('flags.useToolingApi'),
aliases: ['pathtofile', 'p'],
deprecateAliases: true,
}),
We provide some helpers for compatibility within @salesforce/sf-plugins-core
. We've marked them as deprecated
because you should use these helpers only for migrating an existing sfdx
plugin, not for new plugin development.
public static flags = {
// allow but warn on --targetusername and -u
'target-org': requiredOrgFlagWithDeprecations,
// allow but warn on --apiversion
'api-version': orgApiVersionFlagWithDeprecations,
// loglevel is a no-op, but this flag is added to avoid breaking scripts and warn users who are using it
loglevel,
};
As described here, the eslint plugin handles most of the work of getting you to sfCommand
. But there are some differences you should be aware of.
Some properties like this.logger
don't automatically exist. You can create the property by importing Logger
from sfdx-core
. Other properties like requiresUsername
don't do anything and are removed by the linter rules. You'll need to add a flag for the org.
SfCommand
has a private ux
property, which means you can't pass it to helper functions. Instead of this code:
// SfdxCommand
myHelper(this.ux);
Use this code to construct a new ux
instance and pass it to your help:
import { ux, Flags, SfCommand } from '@salesforce/sf-plugins-core'
...
// myHelper can call any `ux` method and terminal output is suppressed if json enabled
myHelper(new ux({jsonEnabled: this.jsonEnabled()}))
SfdxCommand's requires|supports(devhub)username
properties created both a flag for the username AND an apiversion flag.
If your command needs to support an apiversion (or just keep it in place for compatibility), do this:
public static flags = {
'target-org': requiredOrgFlagWithDeprecations,
'api-version': orgApiVersionFlagWithDeprecations,
}
// when you get a Connection, pass the api-version flag to it
// that way, if the user specified an api version, the Connection is set
const conn = flags['target-org'].getConnection([flags.api-version]);
After you run yarn remove @salesforce/command
you might see that you were also using its test
library, based on fancy-test
.
sfdx-core
v3+ includes better tools for mocking auths/orgs/projects/users/connections. See its docs.
This example uses TestSetup to mock an org/connection and calls a command's run
method.
It's helpful to leave existing tests to verify that you've properly aliased all commands and flags to avoid breaking changes.
© Copyright 2024 Salesforce.com, inc. All rights reserved. Various trademarks held by their respective owners.
- Quick Intro to Developing sf Plugins
- Get Started: Create Your First Plugin
- Design Guidelines
- Code Your Plugin
- Debug Your Plugin
- Write Useful Messages
- Test Your Plugin
- Maintain Your Plugin
- Integrate Your Plugin With the Doctor Command
- Migrate Plugins Built for sfdx
- Conceptual Overview of Salesforce CLI