---
schema_version: "1"
id: 95975f586c0f845d2111124482017c9f0679dc0498300a4bb9bd3a44e726765a
title: Migrate Plugins Built for sfdx to sf
collection: developer
version: current
locale: en-us
portal_url: https://docs.salesforce.com/developer/current/en-us/salesforcedx/sfcliplugin/guides/migrate-sfdx-sf.md
canonical_url: https://developer.salesforce.com/docs/platform/salesforce-cli-plugin/guide/migrate-sfdx-sf.html
source_site: developer.salesforce.com
content_type: documentation
content_hash: 4c22d35946ea9a200273c24e29847bf6992c4a53a98153590f641dff76127a32
products:
  - Salesforce CLI Plugin Developer Guide
  - salesforcedx
---
# Migrate Plugins Built for `sfdx` to `sf`

As of April 2023, the `sfdx` version of Salesforce CLI is deprecated and Salesforce no longer supports it. The latest and supported Salesforce CLI version is `sf`. However, many customers created their own plugins using the `sfdx` framework and libraries. If you're such a customer, read on.

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.

## Prerequisites

Update `@oclif` and the `@salesforce/core` library to the latest versions.

1. Update to v3 (or latest) of `@oclif/core` as described by [this document](https://github.com/oclif/core/blob/main/guides/V3_MIGRATION.md).
2. Update to v7 (or latest) of `@salesforce/core` as described [this document](https://github.com/forcedotcom/sfdx-core/blob/main/MIGRATING_V6-V7.md). The root of the repository contains other `MIGRATING_VX_VY.MD` documents that detail every major change and upgrade tips.

## Tools and Tips

These sections provide useful information for migrating your plugins from `sfdx` to `sf`.

### ESLint Configuration

Salesforce provides a set of [eslint rules](https://github.com/salesforcecli/eslint-plugin-sf-plugin) for Salesforce CLI plugins.

While 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 [set up instructions](https://github.com/salesforcecli/eslint-plugin-sf-plugin#use-these-rules-to-migrate-a-plugin-based-on-sfdxcommand-to-use-sfcommand) for more details.

Some of these eslint rules automatically change some of your code while other rules provide suggestions where you can select which fix to apply.

### Migrate Messages to Markdown

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.

1. 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:

   ```bash
   sf dev convert messages --file-name messages/mycommand.json
   ```

   In this case, the command generates the Markdown file `messages/mycommand.md`.

2. Edit the generated Markdown file to make sure your messages look good. See the next section for information about reducing maintenance and [Write Useful Messages](https://developer.salesforce.com/docs/platform/salesforce-cli-plugin/guide/messages/messages.md) for general information about writing messages.

3. 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.

4. Delete the old JSON files after you're finished.

### Dynamic Help and Error Messages

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 itself.

If your plugin supports both `sfdx` and `sf`, use these placeholders in message files rather than hard coding the command and CLI executable name. As a result, you aren't required to rewrite the messages after you change your command structure or name.

Here's an example from the Markdown [message file](https://github.com/salesforcecli/plugin-org/blob/main/messages/delete_sandbox.md) for the `org delete sandbox` command:

```markdown
## examples

- Delete a sandbox with alias my-sandbox:

  <%= config.bin %> <%= command.id %> --target-org=my-sandbox
```

### Backward Compatibility

We encourage you to follow `sf`'s styles in your command and flag names. See [Design Guidelines](https://developer.salesforce.com/docs/platform/salesforce-cli-plugin/guide/design/design-guidelines.md) for details. If you must maintain backward compatibility, you have some options.

If you rename a command, you can alias it back to its original name.

```typescript
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.

```typescript
    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](https://salesforcecli.github.io/sf-plugins-core/modules/compatibility.html) within `@salesforce/sf-plugins-core`. We've marked them as `deprecated` because you use these helpers _only_ for migrating an existing `sfdx` plugin, not for new plugin development.

```typescript
  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,
  };
```

## Key Differences Between "sfdxCommand" and "sfCommand"

As described [in this section](#eslint-configuration), the eslint plugin handles most of the work of getting you to `sfCommand`. But there are some differences.

### Properties That Don't Exist

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 must add a flag for the org.

### `this.ux`

`SfCommand` has a private `ux` property, which means you can't pass it to helper functions. Instead of this code:

```typescript
// SfdxCommand
myHelper(this.ux);
```

Use this code to construct a new `Ux` instance and pass it to your help:

```typescript
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()}))
```

### No Automatic apiVersion

SfdxCommand's `requires|supports(devhub)username` properties created a flag for both the username an apiversion flag. If your command must support an apiversion, or you want to keep it in place for compatibility, do this:

```typescript
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]);
```

## Test the Migration

After you run `yarn remove @salesforce/command` you sometimes 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](https://github.com/forcedotcom/sfdx-core#using-testsetup).

This [example](https://github.com/salesforcecli/plugin-data/blob/bdbcca4a9188dcfc06ce4fa13ddbd5f2b3f5ba83/test/commands/data/create/record.test.ts#L1) uses TestSetup to mock an org and a connection and calls a command's `run` method.

### Testing Aliases

To avoid breaking changes, we recommend that you leave existing tests which verify that you've correctly aliased all commands and flags.
