How to create code generation templates with PlopJS, generate-template-files.
31
Jan
How to create code generation templates with PlopJS, generate-template-files.

While coding, developers sometimes find it boring to reproduce boilerplate code for software projects or implementing common components from time to time. Code Generation has become a secret weapon for developers, as well as researchers, to reduce repetitive tasks, as well as save time for more important tasks.

In this article, you can find out how to create your Code Generation template with PlopJS & generate-template-files (NPM Package). We will also provide comparisons between these tools, and how you can leverage each of them for your software projects.

How Template-based Code Generation Works

Template-based code generators operate on the principle of templates, which are predefined patterns or structures. These Templates serve as blueprints for generating code.

Developers create templates with placeholders for variables or dynamic content. When the code generator is invoked, the template is rendered with the provided variables to generate the final code.

If you are new to code generators, we suggest you read these two articles to have a better look at the power of them in coding process.

Create your own code generation template with PlopJS

PlopJS is a powerful and flexible code generator that simplifies the process of creating and maintaining code templates. It provides a command-line interface and a declarative syntax for defining templates and generating code based on them.

Key Features

  1. Code Generation: Plop.js enables developers to generate code snippets, files, and entire project structures with ease. It offers a simple and intuitive command-line interface, allowing users to define custom templates and automate repetitive coding tasks.
  2. Template-driven Approach: Plop.js follows a template-driven approach, where developers can define templates using Handlebars, a popular templating language. This allows for dynamic content generation, making it easy to customize generated code based on specific requirements.
  3. Interactive Prompts: Plop.js provides interactive prompts during code generation, allowing developers to input custom values or make choices based on predefined options. This feature enhances flexibility and ensures that generated code aligns with specific project needs.
  4. Customizable Actions: Plop.js allows developers to define custom actions that can be executed during code generation. These actions can include file manipulation, installing dependencies, running scripts, or any other custom logic required for the project setup.

Putting PlopsJS into Action

1. Setting up PlopJS

Installation

yarn add --dev plop

Create a Plopfile.js at the root of your project

export default function (plop) { // create your generators here plop.setGenerator("basics", { description: "this is a skeleton plopfile", prompts: [], // array of inquirer prompts actions: [], // array of actions }); }

Create a plopfile with the desired generator

export default 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", }, ], }); }

In my experience with Plop.js, I often find the need for multiple templates, each involving various prompts and actions. Fortunately, Plop.js facilitates a declarative approach, enabling me to manage all these distinct plopfiles efficiently within a single Plopfile.js file.

Example

import { NodePlopAPI } from "plop"; export default (plop: NodePlopAPI) => { plop.load("./actors/model.ts"); plop.load("./actors/module.ts"); };

The plop.load method is used to load separate plopfiles. In this case, it loads two templates: ./actors/model.ts and ./actors/module.ts.

High-Level Idea

The idea here is to modularize the code generation process. Instead of defining all templates, prompts, and actions in a single massive file, you can break them down into smaller, more manageable files.

Each file, like ./actors/model.ts and ./actors/module.ts, contains the specific configuration for generating code related to models or modules.

2. Defining Templates

The syntax within a template file is a Handlebars template. It allows for the definition of dynamic content, conditional logic, and the application of case modifiers.

Let's consider a simple example template for generating a controller:

// templates/controller.ts import { ModuleController } from 'your-module-controller-library'; import { decodeRequest, generateSuccessResponse, generateErrorResponse, httpStatus } from 'your-utils-library'; export const {{camelCase actionName}}: ModuleController = async (request, h) => { try { const { payload, db } = decodeRequest(request); const {{camelCase moduleName}} = await db.models.{{pascalCase moduleName}}.{{actionName}}(payload); return generateSuccessResponse(h, {{camelCase moduleName}}); } catch (error) { return generateErrorResponse(h, `Error {{actionVerb}} {{camelCase moduleName}}`, httpStatus.BAD_REQUEST); } };

In this example template:

  • {{camelCase actionName}} and {{pascalCase moduleName}} are placeholders for dynamic content. During code generation, these placeholders will be replaced with actual values provided by the user.
  • {{actionVerb}} is another placeholder for a dynamic action verb. This could be customized based on user input during code generation.
  • camelCase and pascalCase are called case modifiers, and they are used to convert the action name to camelCase and PascalCase, respectively. You can create your own case modifier if needed (more on this in the next section).

3.Creating Generators

Plop.js generators are essentially sets of instructions for creating or modifying files and directories based on user input. In your provided code, the generator is named 'model'. Let's break down the key concepts:

3.1 Setting Up Helpers

In previous sections, I mention about customizing a case modifier. In this section, I'll cover the concept of 'helper' and how it can be used to customize the generated code. Take the following example:

plop.setHelper('upperCase', str => str.toUpperCase()); plop.setHelper('singularCase', str => inflection.singularize(str)); plop.setHelper('printModelName', modelName => inflection.singularize(inflection.camelize(modelName)));
  • Helpers: Helpers are functions that modify or manipulate data during the code generation process. In this example, three custom helpers are defined: upperCase, singularCase, and printModelName. These helpers perform tasks like converting strings to uppercase, singularizing, and camelizing model names.

3.2 Defining Generators

plop.setGenerator('model', { description: 'Create a model', prompts: async (inquirer: Inquirer) => { /* ... */ }, actions: answers => { /* ... */ }, });
  • Generator Definition: The setGenerator method defines a generator named 'model'. This generator is responsible for creating a Sequelize model.
  • Description: A description is provided to explain the purpose of the generator.
  • Prompts: The prompts property defines the questions to be asked during the code generation process. It uses the Inquirer library to prompt the user for information needed to generate the model.
  • Actions: The actions property defines the steps to be taken after the user provides the necessary input. These steps include appending constants to a file and adding a new model file.

3.3 Prompting for Model Information

prompts: async (inquirer: Inquirer) => { /* ... */ }
  • Prompt Function: The prompts property is a function that uses the inquirer library to prompt the user for information needed to generate the model.
  • Recursive Prompts: The prompts include asking for the model name and, through a recursive function (addColumnsPrompt), dynamically adding columns based on user input.

3.4 Generating Actions

actions: answers => { /* ... */ }
  • Actions Function: The actions property is a function that specifies the actions to be taken based on the answers provided by the user. Some built-in actions such as:

  • add: add new files to the project

  • append: append content to files

  • ...(Read more here)

  • File Operations: The actions include appending a constant to a file and adding a new model file based on a Handlebars template (model.hbs).

Generate-template-file (NPM Package)

This is a simple generator that is independent of any language. This package can create custom boilerplate, scaffolding, skeleton, and templating code files that you need to create over and over again. All you need is NodeJS installed to get started.

Install

yarn add generate-template-files

Usage

Let's delve into how you can use generate-template-files to create templates and automate code generation.

1. Create a Generator Script

Start by creating a script file for your generator, e.g., generate.js. Here's a basic example:

const { generateTemplateFiles } = require('generate-template-files'); const config = require('../package.json'); // You can customize this based on your project structure generateTemplateFiles([ // Your template configurations go here ]);

2. Configure Template

Define your template configurations within the generateTemplateFiles function. Let's break down a sample configuration:

{ option: 'Create Redux Store', defaultCase: '(pascalCase)', entry: { folderPath: './tools/templates/react/redux-store/', }, // ... (more configuration options) }
  • option: A user-friendly name for the template.
  • defaultCase: Specifies the default naming convention (e.g., pascalCase).
  • entry: The path to the folder containing your template files.

3. Run Your Generator

Execute your generator script using Node.js:

node generate.js

Key Features

Let's highlight some key features of generate-template-files through the provided example:

  • String Replacers: Define dynamic placeholders in your templates and replace them with user input or predefined values.
  • Dynamic Replacers: Introduce dynamic replacements during code generation, allowing for flexibility based on project metadata.
  • Output Configuration: Specify the output path and filename conventions, ensuring a consistent structure for generated files.
  • OnComplete Callback: Leverage the onComplete callback to perform additional actions or log information after the generation process completes.

Comparision between PlopJS & generate-template-files

plopjs_generate_temp_files_npm_t0ih55.png

Conclusion

I think I prefer PlopJS because it uses HandleBars, which is a well-known syntax for templating. Also, PlopJS is a feature-rich, community supported code generation tool. Whether you're scaffolding new components, establishing boilerplate code, or enhancing project consistency, Plop.js is poised to elevate your development experience.

Embrace the power of Plop.js and witness how it transforms your code generation tasks into efficient and enjoyable endeavors.

Happy coding! Try our AI Beta chatbot here for quotation on your software project ideas or f_ollow_ Rockship Blogs for more posts about software craftsmanship like this.