This is the 4th of part of this series, where I document my journey to creating my first Salesforce CLI plugin.
fflib_Servicewould be grouped inside a
fflibfolder in an sfdx project directory.
Readpart 1to get more info on the use case and rationale.
You can read the previous parts here:
Where are we
In the previous article, I talked about testing the plugin logic using Jest. At this point, I'm confident that all the logic works as expected; I also tested it manually with a real sfdx project.
Now, I need to create the actual plugin; remember that up to this point, I've been working on a simple NPM package as I wanted to avoid getting dragged down by details of how the plugin architecture works, etc.
So let's talk about how that's going.
New SF plugin architecture
In case you are not aware, Salesforce is moving away from the
sfdx CLI, and is currently implementing the new
sf CLI. The same is true for plugins; they should be created under the
sf CLI and their architecture has changed.
There's also a new developer guide to building
sf plugins, currently hosted on GitHub.
Creating the plugin
To create the plugin, I used the
sf dev generate plugin command, which asks you a few questions about your plugin and generates the entire directory with all the files ready to go.
One thing to note is that this directory has many more files compared to the old plugin architecture.
This is because this new architecture comes with built-in support for CI/CD, so there's a ton of configuration files and
yarn commands that support that process.
Once the basic structure of the plugin is created, you can create new commands using the
sf dev generate command --name [name:of:command] command.
In my case, I created a command called
dxdir:arrange, which should be called like this
sf dxdir:arrange. Again, all the files needed to run this command were automatically generated, which is awesome
Finally, remember that the code is meant to act on the context of a folder where the apex classes reside, and by default it's assumed to be
For this, I created a flag so that I could call the command like this
sf dxdir:arrange --apex-dir [your path]
Again, the new
sf CLI provides a handy interactive command to create flags; the
sf dev generate flag command.
I created a flag called
apex-dir, of type
directory and configured it so that it automatically verifies if the directory actually exists or not (I didn't configure any of this manually; the CLI asked me these questions when I ran the
generate flag command.
This resulted in the following flag
exists property verifies that the directory actually exists, and there's also a
default value, which I set to the default apex class directory.
Transferring the code
The final step was to move the code from the original NPM package into this new folder. This wasn't too easy, and I had to change a few things to get it to work, but it did in the end :)
Passing the flag
Once I copied all the existing code into the command file, I modified the
run method to call the original code, and pass the flag that points to the folder where the classes reside
reoderFiles is the original function I wrote in previous chapters; the difference is now we use the flag passed to the command. You can see we extract the value of the flag using the
As mentioned earlier, if the directory doesn't exist, the flag automatically throws an error, which is great because it means I don't need to add more code to validate that myself.
Getting the tests to work
When you generate a new command, the CLI also creates a set of tests files for that command. Here's what a default test looks like
Basically, this test says that when the command
dxdir:showme is run, we can expect the standard output (the terminal) to print something that contains the words
This is very different from how I wrote my tests, as covered in part 3. Also, these tests are written with the testing library Chai, instead of Jest (what I used).
So here's what I did to configure the plugin to run Jest tests my way:
1- Configure Jest to work with Typescript
2- Then, I created a new NPM command called
jest-test that would call Jest.
3- Then, I had to modify the
pre-push husky file (which runs before I push the code to a remote repository) to run the new NPM command.
This basically means that when I run
git push, the
yarn build and
yarn jest-test commands will run, which build the code (this is a typescript project) and run all tests; if everything passes, the code is pushed to the remote repo.
4- Finally, the
sf CLI comes with built-in
expect functions from Oclif (the project that supports the CLI) that you are meant to use to test if the output of your command is what you expect.
These functions had the same names as those from Jest, which resulted in a bunch of weird errors, such as
test not found or
expect is not a function.
I want to be able to use these built-in functions in the future (not sure yet how) while still using the standard Jest functions with the same name.
What I did was give them an alias of
oExpect. This way, I can use Jest's
expect functions, and in the future, when I want to use the Oclif ones to verify the terminal output, I can use
With all that done, I can now run
yarn jest-test and all my tests pass again~
I think this series is long enough, so the next chapter will be the last chapter; it'll probably be a summary of some new things I added, such as new flags or new commands, and an overview of how to build and publish the plugin.
Thanks for following my journey!