Building your own component generator
2 mins |
December 20, 2020I love to automate any repetitive task 😍. That is what excites me in programming. Sometimes I would spend more time in creating the automation than I would use it for. For any large project, I would come up with a template of how the components should look like. Then I would create a tool to generate those templates. It would give consistency across the team and save some time.
Today will show you how to build one such tool.
This CLI dev tool, we should be able to create a component. We should be able to customize the generated component with some options.
Let’s list down the features we would like in this CLI tool.
What we want?
- Add options to name the component
- Option add files like utility, test files
- Option to choose the location where the component would be created
- Ability to create the component from anywhere
Let’s start by creating a component. For templating, we will be using handlebars and for CLI commands we will be using Plop.
To start with you can add this in any existing project or you can create a new one by npm init. Create a folder called src and create a file named index.js in it. Also, create a folder named templates in src.
Add plop as a dev dependency, npm install plop -D.
To run the component generator, we will add a npm script to in package.json
.
"create-component": "plop --plopfile src/index.js"
.
Let’s us go through a bunch of commands for the same
1npm init -y # if you don't have a project.23mkdir mkdir src templates4touch src/index.js
and in package.json
:
1"scripts": {2 "create-component": "plop --plopfile src/index.js"3 },
While we are at it, let’s create a template file templates/component.hbs
. And
the content can be
1import React from 'react';2import PropTypes from 'prop-types';34function {{ properCase name }}({ children }) {5 return <div>{children}</div>;6}78{{ properCase name }}.propTypes = {9 children: PropTypes.node,10};1112{{ properCase name }}.defaultProps = {13 children: null,14};1516export default {{ properCase name }};
If you notice we have used name
and properCase
. Here name
is a variable
and properCase
is a built in helpers provided by
plop.
So let’s create a plopfile where we ask our user to provide name
.
1const componentGenerator = {2 description: "Add a React component",3 prompts: [4 {5 type: "input",6 name: "name",7 message: "What should it be called?",8 default: "Button",9 },10 ],11 actions: data => {12 const actions = [13 {14 type: "add",15 path: `{{properCase name}}/{{properCase name}}.component.jsx`,16 templateFile: "../templates/component.hbs",17 abortOnFail: true,18 },19 ]2021 return actions22 },23}2425module.exports = plop => {26 plop.setGenerator("component", componentGenerator)27}
Here we are creating a prompt What should it be called?
and an action to
create file.
So lets run this, npm run create-component
.
If everything goes well, it should have asked you What should it be called?
If
you provide the name of the component, it create a folder named after the
component and file inside that. Yay 🥳!!!
Let’s add some more features, like creating an utility file.
1const componentGenerator = {2 description: "Add a React component",3 prompts: [4 {5 type: "input",6 name: "name",7 message: "What should it be called?",8 default: "Button",9 },10 {11 type: "confirm",12 name: "wantUtils",13 default: true,14 message: "Do you want utils?",15 },16 ],17 actions: data => {18 const actions = [19 {20 type: "add",21 path: `{{properCase name}}/{{properCase name}}.component.jsx`,22 templateFile: "../templates/component.hbs",23 abortOnFail: true,24 },25 ]2627 if (data.wantUtils) {28 actions.push({29 type: "add",30 path: `${data.componentDir}/{{properCase name}}/{{properCase name}}.utilities.js`,31 templateFile: "./templates/component.utilities.hbs",32 abortOnFail: true,33 })34 }3536 return actions37 },38}3940module.exports = plop => {41 plop.setGenerator("component", componentGenerator)42}
Before we run the CLI again by npm run create-component
, we have to create the
template template/component.utilities.hbs
. I have kept the file empty for now.
Similarly we can prompt if we need test files and add options for that as well.
You may want add more options based on the project.
If you have noticed the component is always created at src
, we might want to
create component any directory.
We can add inquirer-select-directory
as dependency for the ability to select
the directory.
1const promptDirectory = require("inquirer-select-directory")23const componentGenerator = {4 description: "Add an React component",5 prompts: [6 // ...7 {8 type: "directory",9 name: "componentDir",10 message: "Where you like to put this component?",11 basePath: "./",12 },13 ],14 actions: data => {15 data.username = gitUserName16 data.createdTime = new Date().toDateString()17 const actions = [18 //...19 {20 type: "add",21 path: `${data.componentDir}/{{properCase name}}/{{properCase name}}.component.jsx`,22 templateFile: "./templates/component.hbs",23 abortOnFail: true,24 },25 ]2627 return actions28 },29}3031module.exports = plop => {32 plop.setPrompt("directory", promptDirectory)33 plop.setGenerator("component", componentGenerator)34}
Here we are populating the location at componentDir
and using it while
creating the component. Isn’t that awesome? 🕺
We have to select the directory where we want the component to be generated
every time. It would be awesome if we can run this command from any location in
the filesystem like gen MyAwesomeButton
.
Let’s do that!
First we would have to create an executable script in bin/index.js
.
1#!/usr/bin/env node23const path = require("path")45const plopFilePath = path.join(__dirname, "../src/index.js")6process.argv.push("--plopfile", plopFilePath)7const args = process.argv.slice(2)8const { Plop, run } = require("plop")9const argv = require("minimist")(args)1011Plop.launch(12 {13 cwd: argv.cwd,14 configPath: argv.plopfile,15 require: argv.require,16 completion: argv.completion,17 },18 run19)
We have used minimist
to extract arguments from the command. You also add it
by npm install minimist -D
.
And in package.json
let add this
1"scripts": {2 "create-component": "plop --plopfile src/index.js"3 },4 "bin": {5 "gen": "bin/index.js"6 },
To make this available everywhere in yout system run npm link .
Voilà!!! 🥳🎊
I have created everything we did in a repo, component-generator.
Got a Question? Bala might have the answer. Get them answered on #AskBala
Subscribe Now! Letters from Bala
Subscribe to my newsletter to receive letters about some interesting patterns and views in programming, frontend, Javascript, React, testing and many more. Be the first one to know when I publish a blog.
No spam, just some good stuff! Unsubscribe at any time