Repo: salesforcecli/plugin-templates (Public GitHub, requires access to salesforcecli org)
- Understanding the Two-Repo Workflow
- Declare your Metadata Subtopic and Create Plugin Command
- Define your CLI Input Shape
- Message Labels
- Call your Template Generator
- Testing Your Command
- Release Timelines
- Release Notes
- Local Development Guidance
This guide covers adding template generators to the Salesforce CLI. It's important to understand the relationship between two repositories:
- salesforcedx-templates: Contains the actual template definitions and generator logic for various metadata types
- plugin-templates (this repo): Contains the CLI commands that expose those generators to users
Creating a template generator in salesforcedx-templates does not immediately expose it to the CLI. It is up to each metadata owner to create a corresponding CLI command in this repo to expose their generator to users.
It's critical to remember that creating a new CLI command isn't just about getting the functionality working. You need to be very intentional about the name, description, flag names, flag types, and command names that you are using.
Recommended Reading: Salesforce Developer Documentation
- https://developer.salesforce.com/docs/platform/salesforce-cli-plugin/guide/topics.html
- https://developer.salesforce.com/docs/platform/salesforce-cli-plugin/guide/flags.html
- https://developer.salesforce.com/docs/platform/salesforce-cli-plugin/guide/command-flags.html
- If all this is new to you, check out Get Started Building a Salesforce CLI Plugin which walks you through a simple example.
- Use
sf dev generate command -n template:generate:{metadataType}:{optionalSubTemplate} --no-unitto bootstrap your new command.- This will create all the files you need for introducing a new command, including updating the oclif metadata
- Only create a subtemplate if you want to add nested generators (e.g.
template:generate:site:brand)
- Go to the
package.jsonand update the placeholder description for your new subtopic in theoclifsection underneathtemplate > generate - (Optional) If your command is not ready to be Generally Available (GA), add a "state" to your command with
public static readonly state = 'beta|preview'- Accepted values are
betaorpreview(source). Review the corresponding messages and choose which describes your command best - Once your generator is GA-ready, you can remove this state. Just know that if you do, you’ll be responsible for ensuring backwards compatibility for all new releases.
- Accepted values are
- (Optional) If you do not want your command to show up in the command reference guide, locally when running
sf commands, or in autocompletion results, setpublic static readonly hidden = true- NOTE: We will not document your command in our weekly release notes if your command is hidden.
If you have a single top-level metadata generator, be sure the file path to your command follows
src/commands/template/generate/{metadataType}/index.ts
For each new flag, the best and fastest way to get an error-proof flag added is to use the sf dev generate flag interactive command.
This will walk you through the addition of a new flag and also generate all required TypeScript scaffolding as well as appending the flag to the messages.md file.
Each new flag is added to a readonly flags object, with the type, required parameter, character alias, etc… defined.
public static readonly flags = {
name: Flags.string({ // <-- string parameter
char: 'n', // <-- character alias
summary: messages.getMessage('flags.name.summary'),
description: messages.getMessage('flags.name.description'),
required: true, // <-- CLI fails if parameter is not provided
}),
template: Flags.option({ // <-- string enum
char: 't',
summary: messages.getMessage('flags.template.summary'),
description: messages.getMessage('flags.template.description'),
required: true,
options: ['RecordPage', 'AppPage', 'HomePage'] as const, // enum options
})(), ...
}Dev Hint: If you use
sf dev generate flagfor all new flags, all the scaffolding will be taken care of for you!
See the existing developer documentation for learning how to write useful messages:
https://developer.salesforce.com/docs/platform/salesforce-cli-plugin/guide/messages.html
See here for how to implement and dynamically load your messages in your command implementation. Each new flag requires a "summary" entry in the messages file. Flag descriptions are optional.
If appropriate, link to developer.salesforce.com docs in command descriptions.
After defining your input contract, you’ll be able to use runGenerator, passing in the templateType and input options for your metadata type. This should be encapsulated inside of a run() method which outputs a Promise<CreateOutput>.
import { getCustomTemplates, runGenerator } from '../../utils/templateCommand.js';
...
public async run(): Promise<CreateOutput> {
// ... input definition
// pre-processing/sanitization
return runGenerator({
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
templateType: TemplateType.[YourMetadataType],
opts: flags,
ux: new Ux({ jsonEnabled: this.jsonEnabled() }),
});
}If you use the sf dev generate command command to bootstrap your command, you’ll see that some NUTs (Not Unit Tests) are added.
If you did not use the generate command, then you are expected to add your own tests. The purpose of these is to validate the integration between the CLI flags and what is ultimately passed to the generator, and to ensure that the correct files are created based on those flags.
Unless you’re in the minority, most template generators do not require a scratch org connection, so it should be fairly straightforward to create new NUTs based on the examples in the repo.
Your PR has now been merged into plugin-templates; congratulations! How long do you have to wait until it is available for consumption?
Release information is communicated through official Salesforce CLI channels.
The CLI has a nightly release. The day after your PR is merged, you can update to the latest with sf update nightly or npm install @salesforce/cli@nightly --global. Keep in mind that if you have previously run sf plugins link . during development, you have to run sf plugins unlink @salesforce/plugin-templates.
The release candidate is updated weekly, on Wednesdays, around noon CST (10am PST). To update to the release candidate, you can run sf update stable-rc or npm install @salesforce/cli@latest-rc --global.
After your change is merged into the nightly release, your team should QA/QC ahead of the promotion to the release candidate and make any necessary changes.
A week after promotion to the release candidate, the RC is promoted to latest. Final sanity checks should be done during this week, and emergency patch fixes can be made if required. Update with sf update stable or npm install @salesforce/cli --global.
This promotion also happens around 12pm CST (10am PST) on Wednesdays.
Release notes are updated weekly and merged here: https://github.com/forcedotcom/cli/tree/main/releasenotes.
If you'd like to have your new template generator command specifically included or excluded in the release notes, please coordinate with the CLI team.
In order to get this all to work locally, you’ll need to do a few things:
- Ensure you’re working with
yarn 1, not yarn 3 or 4. - If you're introducing new templates in
salesforcedx-templates, update yourpackage.jsonin plugin-templates to reference your local path to the root directory ofsalesforcedx-templates - Run
yarn buildafter all changes to your templates or generator (in dx-templates) - Run
yarn install --forceandyarn buildinplugin-templatesto fetch newly built changes from your local salesforcedx-templates directory - When introducing your CLI command for the first time, run
sf plugins link .fromplugin-templates, which will override your local CLI with the new command that you’re working on. a. Alternatively, you can run your new command directly from the plugin-templates directory with:./bin/run.js template generate your-command