Plop for auto generated content workflow
I've used yeoman-generator
for some time now and wanted to try a new generator for my garden. I chose to try plop
this time.
Note: Plop
has an api that would allow for items to be created dynamically during CI/CD
, but we will get into that in a later post. In summary, plop
is just a tool that takes inquirer
prompts and glues them to handlebars
templates.
Let's get into this!
Steps we'll take
- Install plop into our package
- Create the shell of our plop workflow
- Create a template
- Add new prompts for the template
- Use a helper function to get a date format for our file name
Install plop into our package
Let's install plop
into our project package.
yarn add plop@latest --dev
OR
npm install plop@latest --save-dev
Using a monorepo with yarn workspaces, then you will add it at the root of your repository.
yarn add plop@latest --dev --ignore-workspace-root-check
Create the Plopfile
Create a file called plopfile.js
at the root of the project.
module.exports = function (plop) {// create your generators hereplop.setGenerator('garden', {description: 'this is a skeleton plopfile',prompts: [], // array of inquirer promptsactions: [] // array of actions});};
Now we'll just create the generator to only write out a simple template file (below) into our garden.
module.exports = function (plop) {// create your generators hereplop.setGenerator('garden', {description: 'Create an entry into the Digital Garden',prompts: [{type: 'input',name: 'slug',message: 'slug name of post (will be part of file name)',},],actions: [{type: 'add',path: 'garden/posts/{{slug}}.mdx',templateFile: 'plop-templates/garden.hbs',},],});};
Create a template
Plop
uses handlebars for it's templates, so we'll put our template plop-templates/garden.hbs
in our project. This is great news, because handlebars
is quite mature and has a powerful api.
plop-templates/garden.hbs
---draft: {{draft}}title: {{title}}path: /garden/{{slug}}date: {{date}}author: talvesdescription: >-{{description}}categories:- 'draft'keywords:- 'new'- 'garden'garden: {{garden}}image: '/images/social/{{slug}}.png'---## Just a simple H2
Add new prompts for the template
Here are the prompts for our template:
The draft prompt is of type list and allows for a boolean value defaulting to true.draft
{type: 'list',name: 'draft',default: 'true',choices: ['true', 'false'],},title
{type: 'input',name: 'title',message: 'enter title:',},
The slug prompt allows for entry using spaces and will replace any spaces with a dashslug
-
.{type: 'input',name: 'slug',message: 'slug name of post (will be part of file name)',filter: val =>val ? val.toLowerCase().replace(/ /g, '-') : 'new-entry',},
The date prompt allows for an empty entry for current system date time. Also converts to thedate
ISO
format.{type: 'input',name: 'date',message: 'enter post date:',filter: val => {const date = val ? new Date(val) : new Date(Date.now());return date.toISOString();},},description
{type: 'input',name: 'description',message: 'enter description:',},
Again, we have a list for the garden status with three items and a default. The default value is always pointed to for fast entry by just pressing enter.garden
{type: 'list',name: 'garden',message: 'enter status of this entry today:',default: 'sprout',choices: ['sprout', 'sapling', 'evergreen'],},
Helper function for our file name prefix
Here is the helper function for the file name prefix (YYYY-mm-dd). We use this in our template passing in the date entered to build the prefix.
fileDate
plop.setHelper('fileDate', function(date) {return new Date(date).toISOString().replace(/^(?<year>\d+)-(?<month>\d+)-(?<day>\d+)T.*$/,'$<year>-$<month>-$<day>',);});
Completed code
Here is the completed code for plopfile.js
module.exports = function(plop) {// helper functionsplop.setHelper('fileDate', function(date) {return new Date(date).toISOString().replace(/^(?<year>\d+)-(?<month>\d+)-(?<day>\d+)T.*$/,'$<year>-$<month>-$<day>',);});// create your generators hereplop.setGenerator('garden', {description: 'Create an entry into the Digital Garden 🖋',prompts: [{type: 'list',name: 'draft',default: 'true',choices: ['true', 'false'],},{type: 'input',name: 'slug',message: 'slug name of post (will be part of file name)',filter: val =>val ? val.toLowerCase().replace(/ /g, '-') : 'new-entry',},{type: 'input',name: 'date',message: 'enter post date:',filter: val => {const date = val ? new Date(val) : new Date(Date.now());return date.toISOString();},},{type: 'input',name: 'title',message: 'enter title:',},{type: 'input',name: 'description',message: 'enter description:',},{type: 'list',name: 'garden',message: 'enter status of this entry today:',default: 'sprout',choices: ['sprout', 'sapling', 'evergreen'],},],actions: [{type: 'add',path: 'www/site/garden/posts/{{fileDate date}}-{{slug}}.mdx',templateFile: 'plop-templates/garden.hbs',},],});};
Done!
Add your command to the scripts section of your package.
"scripts": {"new": "plop garden"},
Then you can run your command to start plop
yarn new
or
npm run new
This gives a quick scaffold of a new post and saves the time of copy paste and changing field values. There are some improvements that can be made here, but for now we'll call this finished and add improvements here when they are made.