Yeoman – Generate SFDX Project Workflow
This post will describe how to use a Yeoman generator for creating and setting up an SFDX project.
We covered the basics of Yeoman on the previous post :
Yeoman – Getting Started with ‘Yo’ AutomationPrevious Article about Yeoman
SFDX Project folder structure
The basic structure contains the following :
- Documentation in README.md file.
- Source Code Settings such as what to ignore in the :
- .gitignore – ignore file from git
- .forceignore – ignore file from SFDX commands
- IDE Settings (VS Code)
- Configuration Folder :
- Scratch Org configuration file
- App Source Code Folder (force-app) :
- main > default > here you will find all metadata being tracked.
- sfdx-project.json
- manifest
More information in this Documentation link
Define the Generator Workflow
The Following workflow can be pretty useful to start your Project as a Developer:
- Create the Project using the SFDX command and use User questions inputs as command flag
- Open your VS Code Editor and Start Coding.
The SFDX Command to invoke using the Generator :
sfdx force:project:create
- -n : Project Name
- -p : Default Package Directory
- -s : Optional => Allow to add Namespace
- -t : Org Template => standard
- -x : –manifest => Include Manifest
Import some useful packages
Inside your Generator js file you may wish to import and reference some dependencies to help you run the SFDX commands and output some nicer graphics.
const Generator = require('yeoman-generator'); const chalk = require('chalk'); // will add some colors to input/output const yosay = require('yosay'); // will add yeoman ascii image const shell = require('shelljs'); // will run shell commands such as sfdx const spinner = require('ora'); // will show a nice looking spinner when processing
Initialize your generator using the initializing()
method
constructor(args, opts) { super(args, opts); //input params this.argument("projectName", { type: String, required: false }); this.argument("skipIntro", { type: Boolean, required: false }); } // init the spinner module initializing() { this.loading = new spinner( { spinner:'monkey', color : 'yellow' } ); }
Prompt Questions for populating the Flags
This Example will simply add them as Questions array to the yeoman prompting()
method, it will prompt the Users to type the project Name etc and will scaffold the SFDX project.
prompting() { const projectQuestions = [ { type: "input", name: "projectName", message: "Project Name ? "+ chalk.yellow(this.destinationPath() +"/ "), default:'MyProject', when: !this.options.projectName, validate: function(value) { return value ? true : 'Please enter a name'; } }, { type: "input", name: "appFolder", message: "App Folder Name ? ", default: 'force-app', validate: function(value) { return value ? true : 'Please enter a name'; } }, { type: "input", name: "appNamespace", message: "Namespace (optional)" }, { type : 'confirm', name : 'includeReadMe', message : 'Include ReadMe file', default : true }, { type: "editor", name: "appDescription", message: "Description (optional)", when: function(answers) { return answers.includeReadMe; } } ]; // adding the questions to a new merged array const questions = [...projectQuestions]; // will store user inputs this.props = {};// ask the questions and use the answers return this.prompt(questions).then(answers => { this.props = answers; this.props.projectName = this.options.projectName ? this.options.projectName : answers.projectName; });}
Running the SFDX Command and write config files
Once we stored the questions response inside a public variable this.props
– we can use the answers to populate the project command template
writing() { const answers = this.props; //start loader spinner this.loading.start('configuring sfdx command : ' + chalk.red(this.props.projectName) + '\n'); let sfdxCommand = ' sfdx force:project:create'; sfdxCommand += ' -n ' + answers.projectName; sfdxCommand += ' -p ' + answers.appFolder; if(answers.appNamespace) // Optional sfdxCommand += ' -s ' + answers.appNamespace; sfdxCommand += ' -t ' + 'standard'; // --template if(answers.includeManifest) sfdxCommand += ' -x '; // log the full command to run this.log(" Run ?? : "+ chalk.magenta(sfdxCommand) ); // execute shell command shell.exec(sfdxCommand); }
Using the writing()
method will allow us to Scaffold some extra config files and place them in our project
- eg. create a readme.md template and have it ready when project is completed with all the basic things you wish to have on it.
// check if command has ran successfully if( shell.exec(sfdxCommand).code === 0 ) { // check if user wish to create readme file if(this.props.includeReadMe){ // will copy and write the file from template this.fs.copyTpl( this.templatePath('docs/README.md'), // path location of my readme.md template this.destinationPath(this.props.projectName + '/readme.md'), // path destination { appPath: this.props.appFolder, appName: this.props.projectName, appDescription: this.props.appDescription } ); } // log success message and stop spinner this.loading.succeed(chalk.green('Created project successfully')); /******* OPTIOAL SETTINGS ********/ // add computer voice to the mix to say 'completed' shell.exec(' say \' Your project was created! \''); // open vscode editor with the new project folder shell.exec(' code '+ answers.projectName); }