We write code and create files every day. If you're developing some frontend project most likely you're creating components which start with almost the same folder and file structures. Creating this over and over again is a boring job and one that most of us probably would love to improve.
I was facing that in a React project I'm currently working on and I wanted to automate these initial files every time I wanted to create a component. For that, there is a Javascript called Plop.
What is Plop.js?
As in its website, Plop is a little tool that saves you time and helps your team build new files with consistency. It makes our life easier when creating files because we just need to setup, create some generators and templates and from that every new files created will follow the same structure.
How to use it?
1. Installation
npm install --save-dev plop
yarn add -D plop
2. Create a Plop file
A Plop file is where we create generators for the library to execute when we run plop
command in terminal.
It is a file where we basically export a function which receives the plop
object. This object exposes the Plop API which then we can create a generator with the setGenerator(name, config)
function.
A generator
is where we define the prompts
, questions to be asked to the user, and the actions
, to be performed by Plop for adding, modifying or appending files.
An example taken from the docs for a basic plop file in Javascript:
// plopfile.js
module.exports = function (plop) {
// controller generator
plop.setGenerator('controller', {
description: 'application controller logic',
prompts: [{
type: 'input',
name: 'name',
message: 'controller name please'
}],
actions: [{
type: 'add',
path: 'src/{{name}}.js',
templateFile: 'plop-templates/controller.hbs'
}]
});
};
This generator called controller will ask us for the controller name and will perform a creation of file in the specified path with a dynamic file name and this new file will be structured the same way as controller.hbs file.
3. Create a template file with Handlebars
Handlebars is a templating language, it uses a (wait for it) template with an input object to generate HTML or other text formats. It looks like normal text with some Handlebars expressions. We won't cover more than this, this is the basic stuff to get going here, but you can dive deep in the docs.
With this template defined the generator will do its actions and create the files for us. Let's see an example in action.
Real usage example
I hope the steps above were enough to understand the basic follow. In this section we'll see a real example, the way I used Plop for my current project.
1. Creating a Plopfile
For my current project I'm using Atomic Design to structure my components as a way to improve its consistency and reusability.
For each component I create I need to define which folder it is going to be added to, besides its name. These are the prompts
my generator
has.
Furthermore, I usually create a 3 files for each component: index.tsx
for the component itself, stories.tsx
for the Storybook stories and test.tsx
for the tests. In this example, to simplify, I'm creating only the first two. So, these are my actions
for the generator
.
import {NodePlopAPI} from 'plop';
export default function (plop: NodePlopAPI) {
// controller generator
plop.setGenerator('component', {
description: 'Atomic Design component creation logic',
prompts: [
{
type: 'input',
name: 'name',
message: 'Component name please',
},
{
type: 'input',
name: 'level',
message: 'Atomic Design level of the component please',
},
],
actions: [
{
type: 'add',
path: '../src/components/{{level}}/{{pascalCase name}}/index.tsx',
templateFile: 'templates/index.tsx.hbs',
},
{
type: 'add',
path: '../src/components/{{level}}/{{pascalCase name}}/stories.tsx',
templateFile: 'templates/stories.tsx.hbs',
}
],
})
}
Bear in mind that the export is a little different from the previous I showed since I'm using Typescript for my project.
So, basically this generator prompts questions for me and according to my answers it creates two files in a dynamically created folder based on the templates.
2. Creating the templates
For the templates I created two Handlebars files, one for stories.tsx
and other for index.tsx
.
1. index.tsx
import { Box } from '@chakra-ui/react'
export type {{pascalCase name}}Props = {
}
const {{pascalCase name}} = ({ }: {{pascalCase name}}Props) => (
<Box>
{{pascalCase name}}
</Box>
)
export default {{pascalCase name}}
2. stories.tsx
import {{pascalCase name}}, { {{pascalCase name}}Props } from '.'
import { Story, Meta } from '@storybook/react'
export default {
title: '{{pascalCase name}}',
component: {{pascalCase name}},
} as Meta
const Template: Story<{{pascalCase name}}Props> = (args) => <{{pascalCase name}} {...args} />
export const Default = Template.bind({})
Default.args = {
}
In each template file, the {{pascalCase name}}
will be replaced by the name, as a pascal case string, I provided when running plop
command.
3. Run the plop command in a package.json script
To make it easier for me, I created a script called generate to run the plop
command, the --plopfile
flag is used to direct the path to my plopfile with the generator
.
// package.json
// ...
"scripts": {
//...
"generate": "yarn plop --plopfile ./plop/plopfile.ts"
},
// ...
When runing yarn generate
, Plop will ask me the questions I defined and create the files accordingly as seen below:
Generated index.tsx
:
import { Box } from '@chakra-ui/react'
export type UserHeaderProps = {
}
const UserHeader = ({ }: UserHeaderProps) => (
<Box>
UserHeader
</Box>
)
export default UserHeader
Conclusion
That's it! You just need to set up Plop and follow the steps to create its generator in the plop file, the templates files and the script to run it. After that, from a simple script call, Plop will do everything for you to make it smoother when creating new components for you application.
Plop can do much more than this and I encourage you to check its docs. For now, this has already made my life easier and I hope it can also help you.
If it was somehow useful, leave it a ❤️ or if you have more to add drop a comment. Also, I'd love if we connect on Twitter as well :)
Top comments (0)