Generating Code with Plop Templates

How we can use Plop to generate a consistent foundation for our files.

4 min read
Coffee by Louis Hansel
Photo by Louis Hansel on Unsplash
We have all been in a similar situation, where we constantly generate new code files and sometimes even fall into the temptation of duplicating a file and changing its base content.

Likewise, we have defined a base architecture with our team that we request to maintain and that is sometimes not easy to do.

To do this, we can use a small but powerful tool called Plop.

Plop is (…) a “micro-generator framework”. Now, I call it that because it’s a little tool that gives you a simple way to generate code or any other type of plain text files consistently. See, we all create structures and patterns in our code (routes, controllers, components, helpers, etc.). These patterns change and improve over time, so when you need to create a NEW pattern, it’s not always easy to find the files in your codebase that represent the current “best practice.” That’s where Plop saves us.

#.Starting with Plop

To get started, let’s create a simple project and install Plop as a development dependency in our project.

mkdir test-plop
cd test-plop
npm init -y
npm install plop --save-dev

With this, we now have plop installed in our project and we can start using it. To do this, we are going to create a file called plopfile.js in the root of our project.

module.exports = function (plop) {
  plop.setGenerator('basics', {
      description: 'this is a skeleton plopfile',
      prompts: [],
      actions: []
  });
};

We will also add a script in our package.json to run plop:

// ...
{
  "scripts": {
    "plop": "plop"
  }
}
// ...

And we run it just to make sure everything is working as it should:

npm run plop
plop-test@1.0.0 plop
plop

#.Generating the first generator: a React component

Let’s say our team has decided to use React and has defined a base architecture for our components. In this case, we are going to create a generator that allows us to create a React component with the following structure:

  • It will use the .jsx extension.
  • Will use PropTypes for type definition.
  • It will have a name in PascalCase.
  • It will have a directory with the same name.
  • You will have a file index.jsx that will export the component.
  • It will have a file [ComponentName].copy.json that will contain the plain text used by the component.
  • You will have a file [ComponentName].module.scss that will export the component styles.

To do this, we are going to modify our plopfile.js file and add the name of our generator, a description and the questions we want to ask the user for the generation of our components.```js module.exports = function (plop) { plop.setGenerator(‘component’, { description: ‘Create a new component’, prompts: [ { type: ‘input’, name: ‘name’, message: ‘What is your component name?’, }, ], actions: [ { type: ‘add’, path: ‘src/components/{{pascalCase name}}/index.jsx’, templateFile: ‘plop-templates/component/index.jsx.hbs’, }, { type: ‘add’, path: ‘src/components/{{pascalCase name}}/{{pascalCase name}}.copy.json’, templateFile: ‘plop-templates/component/copy.js.hbs’, }, { type: ‘add’, path: ‘src/components/{{pascalCase name}}/{{pascalCase name}}.module.scss’, templateFile: ‘plop-templates/component/component.module.scss.hbs’, }, ], }); };


We see that we are referencing a directory called `plop-templates` that does not yet exist. This directory will contain the files that we will use as templates for the generation of our components. To do this, we are going to create it and add the files that we will use as templates. We will also create the directory `src/components` which will contain our components.

```shell
mkdir plop-templates
mkdir src
mkdir src/components

And we create our templates or templates for the generation of our 3 templates. These templates are written in Handlebars.

// plop-templates/component/index.jsx.hbs
import React from 'react';
import PropTypes from 'prop-types';
import COPY from './{{pascalCase name}}.copy.json';

import styles from './{{pascalCase name}}.module.scss';

const {{pascalCase name}} = () => {
  return (
    <div className={styles.container}>
      <h1>{COPY.title}</h1>
    </div>
  );
};

{{pascalCase name}}.propTypes = {
  // property: PropTypes.string.isRequired
};

export default {{pascalCase name}};
// plop-templates/component/copy.js.hbs
{
  "title": "{{pascalCase name}} title"
}
// plop-templates/component/component.module.scss.hbs
.container {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  height: 100vh;
}

With this, we can proceed to run our generator and see the result.

npm run plop

> plop-test@1.0.0 plop
> plop

? What is your component name? Test
✔  ++ /src/components/Test/index.jsx
✔  ++ /src/components/Test/Test.copy.json
✔  ++ /src/components/Test/Test.module.scss

And if we explore the generated file, we will see that the name that we entered in the console has been used in the generation of our files, and using PascalCase.

// src/components/Test/index.jsx
import React from 'react';
import PropTypes from 'prop-types';
import COPY from './Test.copy.json';

import styles from './Test.module.scss';

const Test = () => {
  return (
    <div className={styles.container}>
      <h1>{COPY.title}</h1>
    </div>
  );
};

Test.propTypes = {
  // property: PropTypes.string.isRequired
};

export default Test;

#.Next steps

With Plop we can generate any type of file, from React components to configuration files, stories or even tests.

Likewise, Plop allows the creation of custom prompts, which below use Inquirer.js. This allows us to create questions that allow us to generate files according to the answers that the user enters or even a flow of questions, validations, etc.

For example, if we wanted to add validation so that the component name contains at least 3 characters, we could do it as follows and add the following prompt, called name:

const name = {
  type: 'input',
  name: 'name',
  message: 'What is your component name?',
  validate: (input) => {
    if (input.length >= 3) {
      return true;
    }

    return 'name should be at east 3 characters long.';
  },
};

module.exports = function (plop) {
  plop.setGenerator("components", {
    description: "generate a component",
    prompts: [name],
// ... el resto quedaría igual

and when we run it, we can see the new validation:

npm run plop

> plop-test@1.0.0 plop
> plop

? What is your component name? a
>> name should be at east 3 characters long.

The rest would work exactly the same, only we added a new layer for validation. From then on, the sky is the limit 🌁🙌.