Creando generadores (scaffolds) con Plop

kuscamara

Kus Cámara

Posted on April 29, 2022

Creando generadores (scaffolds) con Plop

En todo proyecto, más pronto que tarde, necesitamos un generador que nos ayude a mantener la consistencia entre los archivos que vamos creando (componentes, tests, páginas, etc.). Un generador no solo es necesario para esto, sino para evitar trabajos repetitivos a base de copy & paste que normalmente se pueden automatizar.

Yeoman es una de las herramientas más completas que podemos utilizar para crear generadores, pero a veces simplemente no necesitamos tanto o queremos tener nuestros templates dentro del proyecto en el que los vamos a usar para poder editarlos más fácilmente.

Plop es una herramienta que nos permite crear nuestros propios generadores a partir de unos templates Handlebars y un archivo de configuración en el que establecemos las preguntas que haremos al usuario mediante Inquirer. Y ya está. No necesitamos más que templates y preguntas para rellenarlos.

Creando un generador

Vamos a ver cómo podemos crear un generador con Plop fácilmente con el ejemplo de un componente.

Lo primero que vamos a necesitar, obviamente, es instalar Plop como dependencia de desarrollo:

npm i -D plop
Enter fullscreen mode Exit fullscreen mode

Nuestros componentes se encuentran dentro de src/components/ComponentName/ y dentro de cada una de las carpetas de nuestros componentes tenemos un archivo con el nombre del componente, un archivo de test y un index que sirve como entry point.

Estructura del proyecto:

├── package.json
├── src
│   ├── index.ts
│   ├── components
│   │   └── MyComponent
│   │       ├── MyComponent.test.tsx
│   │       ├── MyComponent.tsx
│   │       └── index.ts
Enter fullscreen mode Exit fullscreen mode

Somos libres de crear el archivo de configuración de Plop y sus templates donde nos dé la gana, y de hecho la documentación de Plop nos sugiere que creemos el archivo plopfile.js de configuración en la raíz del proyecto, pero para mantener todo más organizado y localizable, los vamos a crear dentro de una carpeta llamada generators (también podría llamarse templates, scaffolds, etc.).

La estructura de nuestro proyecto quedará así:

├── package.json
├── generators
│   ├── component
│   │   ├── component.hbs
│   │   ├── component.test.hbs
│   │   └── index.hbs
│   └── plopfile.js
├── src
│   ├── index.ts
│   ├── components
│   │   └── MyComponent
│   │       ├── MyComponent.test.tsx
│   │       ├── MyComponent.tsx
│   │       └── index.ts
Enter fullscreen mode Exit fullscreen mode

Los templates

Lo primero que vamos a necesitar son los templates en formato Handlebars. Handlebars nos permite usar mucho más que variables (condicionales, bucles, etc.), pero nuestro generador es tan simple que solo utiliza la variable del nombre del componente y un helper ({{pascalCase}}) que nos proporciona el propio Plop.

{{!-- component.hbs --}}
export interface {{pascalCase name}}Props {
  /** Prop description */
  someProp?: string;
}

/**
 * Edit me!
 */
export const {{pascalCase name}} = ({}: {{pascalCase name}}Props) => {
  return <p>Hi!</p>
};
Enter fullscreen mode Exit fullscreen mode

El resultado de transformar el código anterior sería el siguiente para un componente llamado MyComponent, my-component o myComponent:

// MyComponent.tsx
export interface MyComponentProps {
  /** Prop description */
  someProp?: string;
}

/**
 * Edit me!
 */
export const MyComponent = ({}: MyComponentProps) => {
  return <p>Hi!</p>
};
Enter fullscreen mode Exit fullscreen mode

Las preguntas y acciones

Una vez que tenemos preparados los templates, podemos pasar a configurar las preguntas que haremos para obtener las variables que utilizamos en ellos. Para esto utilizaremos el archivo de configuración de Plop.

En este archivo podemos cargar otros generadores o bien configurar directamente las preguntas y acciones, que es lo que vamos a hacer en nuestro ejemplo, ya que de momento solo tenemos un generador.

Para las preguntas utilizaremos el tipo de Inquirer que nos interese dependiendo de si queremos obtener un input, una opción de entre varias, un yes o no, etc.

En nuestro caso solo necesitamos una pregunta de tipo input para que el usuario escriba el nombre del componente y además no necesitamos preocuparnos de validar el formato porque el helper {{pascalCase}} que utilizamos se ocupa de ello:

// plopfile.js
module.exports = (plop) => {
  plop.setGenerator('component', {
    description: 'Creates a new component',
    prompts: [
      {
        type: 'input',
        name: 'name',
        message: 'Component name'
      }
    ],
    actions: []
  })
}
Enter fullscreen mode Exit fullscreen mode

Por último necesitamos especificar las acciones que queremos ejecutar una vez que hemos obtenido los parámetros necesarios a partir de las respuestas del usuario.

En nuestro caso solo necesitamos añadir nuevos archivos, para lo que utilizaremos acciones de tipo add especificando el destino mediante path y el archivo utilizado como template mediante templateFile.

// plopfile.js
module.exports = (plop) => {
  plop.setGenerator('component', {
    description: 'Creates a new component',
    prompts: [
      {
        type: 'input',
        name: 'name',
        message: 'Component name'
      }
    ],
    actions: [
      {
        type: 'add',
        path: '../src/components/{{pascalCase name}}/{{pascalCase name}}.tsx',
        templateFile: 'component/component.hbs'
      },
      {
        type: 'add',
        path: '../src/components/{{pascalCase name}}/{{pascalCase name}}.test.tsx',
        templateFile: 'component/component.test.hbs'
      },
      {
        type: 'add',
        path: '../src/components/{{pascalCase name}}/index.ts',
        templateFile: 'component/index.hbs'
      }
    ]
  })
}
Enter fullscreen mode Exit fullscreen mode

Por último solo nos faltaría añadir un npm-script para facilitar la ejecución de nuestro generador mediante un comando. Como hemos creado el archivo de configuración de Plop en una ubicación distinta a la raíz, necesitamos especificar la ubicación de este archivo como parámetro:

{
  "scripts": {
    "generate": "plop --plopfile ./generators/plopfile.js"
  }
}
Enter fullscreen mode Exit fullscreen mode

De forma bastante sencilla y rápida podemos tener generadores que podemos actualizar fácilmente en cualquier momento según las necesidades que vayan surgiendo. Plop además no se limita a crear archivos, sino que también puede actualizar los existentes, con lo que nos puede servir para mantener nuestra base de código homogénea siempre que hagamos cambios en los templates.

That's all. Happy templating!

💖 💪 🙅 🚩
kuscamara
Kus Cámara

Posted on April 29, 2022

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related

Creando generadores (scaffolds) con Plop
productivity Creando generadores (scaffolds) con Plop

April 29, 2022