<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>Spencer Sharp</title>
        <link>https://amydutton.me</link>
        <description>Your blog description</description>
        <lastBuildDate>Fri, 26 Jun 2026 08:44:35 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>Spencer Sharp</title>
            <url>https://amydutton.me/favicon.ico</url>
            <link>https://amydutton.me</link>
        </image>
        <copyright>All rights reserved 2026</copyright>
        <item>
            <title><![CDATA[Ultimate Beginner’s Guide to Using the Terminal]]></title>
            <link>https://amydutton.me/articles/beginners-guide-terminal</link>
            <guid>https://amydutton.me/articles/beginners-guide-terminal</guid>
            <pubDate>Mon, 20 Nov 2023 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>For years, I was afraid of the Terminal. I was scared that I would erase my entire hard drive with a single typo. 😱</p>
<p>But, then I started using a task runner to compile my Sass (among other things). My world changed forever. I found it to be so much faster and allowed far more task automation than the tools I had been using previously. Anything that will speed up your workflow is worth investing in (whether that’s time or money).</p>
<p>Once I started spending more time in the Terminal, I became more comfortable and confident. Trust me, I still prefer a good GUI (graphical user interface), but I’m no longer afraid I’m going to delete my entire hard drive. — And let’s be honest, you could delete your entire hard drive with a GUI too. Drag your hard drive to the trash and click "Empty Trash." But, nobody in the right their mind would do that. Similarly, you’d have to type a very specific command in the Terminal to delete your entire hard drive and nobody in their right mind would do that either. — Plus if you have a typo in the Terminal, generally, it will you that you have a typo and the command won’t run.</p>
<h2>Terminal Commands that I’ve Found Helpful</h2>
<p>So, here are the commands that I've found to be the most useful.</p>
<blockquote>
<p>Typically when you see commands to run Terminal, you'll see a $ at the beginning of the line. Don’t copy the $, it signifies that it’s the beginning of a Terminal line.</p>
</blockquote>
<h3><strong>Changing Directories</strong></h3>
<p><code>cd</code> stands for <strong>change directory.</strong> Similar to the Finder where you click on the folder, in Terminal, you just type in the directory that you want:</p>
<pre class="language-bash"><code class="language-bash">$ <span class="token builtin class-name">cd</span> Sites
</code></pre>
<p>You can type <code>cd ..</code> to go up a level or <code>cd ../..</code> to go up 2 levels. <code>cd /</code> will take you to your home directory.</p>
<p>The Terminal also supports tab auto-completion. So you could type <code>cd De&lt;TAB&gt;</code> and it will fill in <code>cd Desktop</code> (assuming that’s a folder option) for you. Handy!</p>
<h3><strong>Listing a Folder's Contents</strong></h3>
<p><code>ls</code> will list all files and directories in your current location. If you want to see "hidden" files, or files that start with a period (.) Simply, add a "flag". You can think of a flag as a way of modifying your command or adding preferences. In our case, <code>ls -la</code>.</p>
<p>The dash <code>-</code> and any letter combination after the dash is called a flag.</p>
<p>If you want also want to see who owns the file and its permissions use <code>ls -la</code>.</p>
<img alt="File listing with the Terminal, include hidden files" loading="lazy" width="2486" height="2334" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fls-la.125e04b3.png&amp;w=3840&amp;q=75 1x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fls-la.125e04b3.png&amp;w=3840&amp;q=75">
<p>In our output, the weird looking letter combinations on the side, like <code>drwxr-xr-x</code> , is a shorthand for that file's/folder's permissions. In this case, everyone can read the directory, but its content can only be changed by the user. If you want to do a deep dive on permissions, you might find <a href="https://blog.ssdnodes.com/blog/linux-permissions/">this article from Serverwise</a> useful.</p>
<p><code>pwd</code> will show you the file path to your current location. This comes in real handy when you want to run a script from anywhere.</p>
<img alt="Current file path within the Terminal" loading="lazy" width="2520" height="720" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fscript-anywhere.fd66a0f0.png&amp;w=3840&amp;q=75 1x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fscript-anywhere.fd66a0f0.png&amp;w=3840&amp;q=75">
<h3><strong>Making a New Folder (or Directory)</strong></h3>
<p><code>mkdir FOLDERNAME</code> will <strong>create a folder</strong> named FOLDERNAME. <code>mkdir</code> stands for "Make Directory."</p>
<blockquote>
<p>⚡ <strong>Power Tip:</strong> Anytime, you hit the up arrow on your keyboard, it will fill in the last command you ran. Hit it again and it will cycle to the command before that. The down arrow cycles in the opposite direction.</p>
</blockquote>
<p>Just to give you an idea of how these commands are used together: when I first open the Terminal, I might type <code>ls</code> to see what the nested files / folder options are. Then:</p>
<pre class="language-bash"><code class="language-bash">$ <span class="token builtin class-name">cd</span> Code/GIT/
$ <span class="token function">mkdir</span> NEWPROJECT
$ <span class="token builtin class-name">cd</span> NEWPROJECT
</code></pre>
<p>This navigates to the GIT folder and then creates a new directory for a project. Then, navigates inside the folder I just created.</p>
<p>If this is still making your head spin, here’s a WYSIWYG way that I saved until the end:</p>
<ul>
<li>
<p>Open up your Terminal type in <code>cd .</code></p>
</li>
<li>
<p>Then, open up Finder, navigate to the Folder you want to open in Terminal</p>
</li>
<li>
<p>Drag that folder from the Finder onto your Terminal window. It should enter the location for that file path for you.</p>
</li>
<li>
<p>Now, hit <code>&lt;RETURN&gt;</code>. — You’re welcome.</p>
</li>
</ul>
<iframe src="https://player.vimeo.com/video/886120236?h=aed5880716" width="640" height="401" frameborder="0" allow="autoplay; fullscreen; picture-in-picture"></iframe>
<h3>Creating a New File</h3>
<p>You can create a new file by saying <code>touch</code> and then giving the filename:</p>
<pre class="language-jsx"><code class="language-jsx">touch<span class="token punctuation">.</span><span class="token property-access">gitignore</span>
</code></pre>
<h2>A <strong>few other tips and tricks:</strong></h2>
<h3><strong>Install Warp, Hyper, or iTerm2 as a Terminal Replacement</strong></h3>
<p>If you’re working on a Mac, “Terminal” comes preinstalled.</p>
<ul>
<li>
<p>Pull up the <strong>Finder</strong></p>
</li>
<li>
<p>Type <code>Cmd + Shift + U</code> and you’ll automatically jump to the Utilities folder</p>
</li>
<li>
<p>Inside, you’ll find an application literally called “Terminal”</p>
</li>
</ul>
<p>This works great if you’re just getting started, but I actually prefer to use <a href="https://warp.dev"><strong>Warp</strong></a>. It has a little bit more functionality. Besides, it doesn't hurt that it's <strong>FREE!</strong></p>
<h3><strong>Customize the Look and Feel of your Terminal</strong></h3>
<p>I keep it simple and maintain that less is more. However, you can add additional information for each prompt.</p>
<p>For example, you can change the prompt colors, display what git branch you're currently using, the current git state, etc.</p>
<img alt="Customized Terminal" loading="lazy" width="1544" height="968" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fcustomized-terminal.8ac04d32.png&amp;w=1920&amp;q=75 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fcustomized-terminal.8ac04d32.png&amp;w=3840&amp;q=75 2x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fcustomized-terminal.8ac04d32.png&amp;w=3840&amp;q=75">
<p><a href="https://starship.rs/">Starship</a> makes this easy.</p>
<img alt="Starship" loading="lazy" width="3586" height="2382" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fstarship.c083998e.png&amp;w=3840&amp;q=75 1x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fstarship.c083998e.png&amp;w=3840&amp;q=75">
<ol>
<li>
<p>Use a simple brew command to install:</p>
<pre class="language-bash"><code class="language-bash">brew <span class="token function">install</span> starship
</code></pre>
</li>
<li>
<p>Add the initialization script to the end of your <code>./zshrc</code> file:</p>
<pre class="language-bash"><code class="language-bash"><span class="token builtin class-name">eval</span> <span class="token string">"<span class="token variable"><span class="token variable">$(</span>starship init <span class="token function">zsh</span><span class="token variable">)</span></span>"</span>
</code></pre>
</li>
<li>
<p>Create a configuration file:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">mkdir</span> -p ~/.config <span class="token operator">&amp;&amp;</span> <span class="token function">touch</span> ~/.config/starship.toml
</code></pre>
<p>Then, all of your <a href="https://starship.rs/config/">customizations and configurations</a> are done within your TOML file.</p>
</li>
</ol>
<hr>
<p>Overcoming any fears you might have of the Terminal can lead to significant improvements in productivity and efficiency. While graphical user interfaces have their lace, the Terminal is a powerful alternative, that in practice can increase your productivity and streamline tasks.</p>]]></content:encoded>
            <author>spencer@planetaria.tech (Spencer Sharp)</author>
        </item>
        <item>
            <title><![CDATA[Planning for Success: My Approach to Designing Applications]]></title>
            <link>https://amydutton.me/articles/planning-for-success</link>
            <guid>https://amydutton.me/articles/planning-for-success</guid>
            <pubDate>Fri, 24 Mar 2023 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Last week, I started designing an application called <strong>Made with Xata</strong> where users will be able to share projects that they…well…made with <a href="https://xata.io">Xata</a>.</p>
<img alt="Xata in Figma" loading="lazy" width="3590" height="2382" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fxata-in-figma.30e7bedd.png&amp;w=3840&amp;q=75 1x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fxata-in-figma.30e7bedd.png&amp;w=3840&amp;q=75">
<p>It’s funny, how you can start with a “simple” idea, like a gallery, and it quickly inflates into a much large project:</p>
<ul>
<li>
<p>We’re showcasing projects, how do those projects get added?</p>
</li>
<li>
<p>Once the projects are added, someone will need to approve those projects before they go live on the site. That means we’ll need an admin screen and users.</p>
</li>
<li>
<p>Now, we’re talking about authentication, which means a user will also need to be able to do all the standard stuff: forgot password, reset password, logout. Not to mention, managing their account. Or, approving a user’s account, granting them access.</p>
</li>
<li>
<p>Are we showing profile pictures (avatars) for each user? We need to store those images somewhere.</p>
</li>
<li>
<p>What if an entry changes? Or, there’s a typo that needs to be corrected? We need a way to manage the information.</p>
</li>
</ul>
<p>Suddenly, we have a lot of features that we need to take into consideration. 😅</p>
<h3>UI Flow Diagram</h3>
<p>Anytime I start building an application and thinking through all the various states, one of the tools that I’ll reach for is a UI Flow diagram. Web applications are different than marketing sites. — Granted the lines are blurring, but generally a web application has multiple states within each page, dependent on a user’s interaction. Therefore, a UI Flow diagram displays more information than the traditional site map.</p>
<img alt="Sitemap" loading="lazy" width="2640" height="1684" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fsitemap.9c53b909.png&amp;w=3840&amp;q=75 1x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fsitemap.9c53b909.png&amp;w=3840&amp;q=75">
<p>This helps illustrate how all the data that flows through the application.</p>
<ul>
<li>
<p>Where are users entering the application?</p>
</li>
<li>
<p>What decisions are being made?</p>
</li>
<li>
<p>What the jobs that a user is trying to accomplish?</p>
</li>
<li>
<p>How is the user moving through the application? How many clicks does it take to accomplish their goal?</p>
</li>
<li>
<p>What happens when the user follows the happy path? What are the edge cases?</p>
</li>
<li>
<p>Where can guests go? Where can logged-in users go?</p>
</li>
</ul>
<h3>Stubbing out a New Project within Figma</h3>
<p>Once the UI Flow Diagram is in a good place, I started designing screens within Figma. If it were a larger application, I would have started with low fidelity wireframes instead. But, this is a smaller project and an area where I could save a little bit of time.</p>
<p>One of the hardest parts in design is getting started. This project was a little easier because it matches Xata’s existing branding and there were existing design patterns that I could reference. I went through their entire site, looking at typography, iconography, and various treatments, taking screenshots as I went.</p>
<p>I’ve always been able to overcome a blank canvas, by dumping all my reference images and branding elements on the screen. Then, it feels like I’m trying to solve a puzzle instead of building something from scratch.</p>
<img alt="Three Up" loading="lazy" width="1600" height="480" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2FthreeUp.05b92fbb.png&amp;w=1920&amp;q=75 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2FthreeUp.05b92fbb.png&amp;w=3840&amp;q=75 2x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2FthreeUp.05b92fbb.png&amp;w=3840&amp;q=75">
<p>Sometimes, it’s about building momentum, going through the motions, and starting with something easy. This can be as simple as setting up the page structure within Figma.</p>
<p>I organize every project the same way. This level of consistency makes it easy to come back to projects and easily find what I’m looking for.</p>
<img alt="Pages in Figma" loading="lazy" width="662" height="532" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2FpagesInFigma.519c0672.png&amp;w=750&amp;q=75 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2FpagesInFigma.519c0672.png&amp;w=1920&amp;q=75 2x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2FpagesInFigma.519c0672.png&amp;w=1920&amp;q=75">
<p>Another tip? Don’t always start with the “thing” that makes the most sense. Logically, I should start at the top and work my way down the page. Or, start with the hero or the featured section, building supporting elements around it. But, those can also be the hardest sections to put together, especially when you’re still trying to build a library of styles and components.</p>
<p>A lot of times, I’ll start with the footer, because it’s one of the easiest elements to design. I know I need the copyright information, links to the legal pages, and social media icons.</p>
<img alt="Xata Footer" loading="lazy" width="1980" height="516" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2FxataFooter.41e1e647.png&amp;w=2048&amp;q=75 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2FxataFooter.41e1e647.png&amp;w=3840&amp;q=75 2x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2FxataFooter.41e1e647.png&amp;w=3840&amp;q=75">
<p><em>Disclaimer: I didn't design this footer</em> ☝️</p>
<p>I’ve come to realize that a big difference between juniors and seniors is that seniors have developed a process that they know works for them. I’m a designer, but I don’t <em>feel</em> creative every single day. I’m not sitting around waiting for inspiration to strike. But, I have routines and processes in place that help me produce results regardless of what mood I’m in.</p>
<p>So, if you’re interested in following along, I’m designing and building as much of this project in public. I’m <a href="https://twitch.tv/selfteachme">live streaming three days at week on Twitch</a>, blogging here on Hashnode, and <a href="https://youtube.com/c/selfteachme">dropping videos on YouTube</a>. I’ve also put a project page together in <a href="https://www.notion.so/Project-Page-Made-with-Xata-e1c3579bb49e4713bdd7a77b2e6f415a">Notion to track progress</a>. All code is <a href="https://github.com/ahaywood/made-with-xata">open source and on GitHub</a>.</p>]]></content:encoded>
            <author>spencer@planetaria.tech (Spencer Sharp)</author>
        </item>
        <item>
            <title><![CDATA[PlopJS: The Tool Every Developer Needs for a Faster Workflow]]></title>
            <link>https://amydutton.me/articles/plop-js</link>
            <guid>https://amydutton.me/articles/plop-js</guid>
            <pubDate>Thu, 02 Mar 2023 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>One of the things that I love about programming is that the sky is the limit. There’s a magical moment when you encounter a problem that code can solve. For example, when you find yourself doing a monotonous, repetitive task, code allows you to automate, streamlining your workflow.</p>
<p>One of my favorite tools for writing these time-saving scripts is <a href="https://plopjs.com/">PlopJs</a>. It’s a tiny tool with a lot of power.</p>
<h2>What types of automation are we talking about?</h2>
<p>One of the lessons that I learned early on is that folder structure and file organization matter. Nerdy, I know. 🤓 But, when I open a project in the Finder, I don’t want to go on an archeological dig, excavating nested folders upon nested folders, in search of what I’m looking for. The easiest way to overcome that challenge is consistency. Create a system.</p>
<p>Nobody likes talking about systems and processes (at least most “normal” people 😉 don’t). But, the beauty is that you can go to any project folder and know <em>exactly</em> where to go.</p>
<p>I cohost a podcast, called <a href="http://Compressed.fm">Compressed.fm</a>. For each episode, I have a specific directory structure to keep all the audio files and phases of the project organized:</p>
<img alt="Compressed.fm Episode Folder" loading="lazy" width="1650" height="810" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fsam-jaber.d6cf90c7.png&amp;w=1920&amp;q=75 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fsam-jaber.d6cf90c7.png&amp;w=3840&amp;q=75 2x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fsam-jaber.d6cf90c7.png&amp;w=3840&amp;q=75">
<p>In the past, I've used a "template folder" that I would duplicate each time we recorded a new episode. — And that worked, except there are always a handful of things I would need to customize for each episode.</p>
<p>This is one of those mundane tasks that we can automate!</p>
<p>I wrote a <a href="https://plopjs.com/">PlopJs</a> script and now I can run a single command within the Terminal. It will prompt me for the episode number and short name. Then, it will generate all the associated files and folders based on my responses.</p>
<img alt="Plop in Terminal" loading="lazy" width="1554" height="728" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fplop-in-terminal.877a71a0.png&amp;w=1920&amp;q=75 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fplop-in-terminal.877a71a0.png&amp;w=3840&amp;q=75 2x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fplop-in-terminal.877a71a0.png&amp;w=3840&amp;q=75">
<p>On this project, I’m also using Plop to generate the show notes for each episode. Before, I would create a markdown file for the show notes, but would always have to go hunting for the sponsor information. What description did they want to use? What tracking links did they want to use? It’s not hard to look up this information, but after 100+ episodes, a small task like this does add up.</p>
<p>Within the Terminal, I can run <code>cfm:showNotes</code> and it will ask me which episode I want to add notes for. It generates this list dynamically based on the episode folders in my project.</p>
<img alt="Terminal Selection" loading="lazy" width="1548" height="930" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2FterminalSelection.9da2c672.png&amp;w=1920&amp;q=75 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2FterminalSelection.9da2c672.png&amp;w=3840&amp;q=75 2x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2FterminalSelection.9da2c672.png&amp;w=3840&amp;q=75">
<p>Next, it will ask me which sponsors should be included. I have a plop templates folder with a markdown file for each sponsor. Each file includes the sponsor description that I want to include within the show notes. I provided Plop with a list of all the sponsors and the markdown files with their information. Plop displays that list in such a way that I can pick as many (or as few) sponsors as I want.</p>
<img alt="Sponsor Selection" loading="lazy" width="1548" height="930" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fsponsor-selection.1eb2833e.png&amp;w=1920&amp;q=75 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fsponsor-selection.1eb2833e.png&amp;w=3840&amp;q=75 2x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fsponsor-selection.1eb2833e.png&amp;w=3840&amp;q=75">
<p>From there Plop has all the information it needs to generate a text file with the Show Notes.</p>
<img alt="Show Notes" loading="lazy" width="1548" height="930" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2FshowNotes.28576529.png&amp;w=1920&amp;q=75 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2FshowNotes.28576529.png&amp;w=3840&amp;q=75 2x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2FshowNotes.28576529.png&amp;w=3840&amp;q=75">
<p>Here’s the finished result. The information at the top, show description, time stamps, and the sponsors heading are all part of the template. The specific sponsors are added dynamically. Essentially it’s taking a single file and then appending additional content from another file.</p>
<img alt="Show Notes in VS Code" loading="lazy" width="2076" height="1836" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fvscode.0ce4e728.png&amp;w=3840&amp;q=75 1x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fvscode.0ce4e728.png&amp;w=3840&amp;q=75">
<p>Let’s look at another example, maybe folder organization isn’t as exciting to you, as it is to me. 😜 But, if you spend your days (and nights) writing code, you can also use Plop to generate component files.</p>
<p>I didn’t fully realize the beauty of using a generator to stub out component code for me until I started working with <a href="https://redwoodjs.com/">Redwood.js</a>. They have a whole library of generators available within their CLI. Running a single command will generate all the boilerplate code you need for creating a component, layout, page, etc. With PlopJs, though, you can bring that same functionality to any project that you work on.</p>
<p>By running a single command, you can have it generate the component code, a storybook file, and a Jest/Cypress/Playwright test file(!!)</p>
<p>I’ve been working on rebuilding my personal website on Astro, primarily because it makes working with MDX so easy. But, the pain point comes in managing the frontmatter for each content type. For articles and blog posts, I have several data points I’m adding inside the frontmatter section. Then, for the “uses” section, I have a different set of frontmatter data. In fact, I have several more content types, all with different sets of front matter.</p>
<p>Plop solves this problem. I have different templates set up for each content type. I can even prompt the user and pre-fill the front matter based on the user’s input. C<em>hef’s kiss!</em></p>
<h2>How do I start a Plop project and what does the code look like?</h2>
<p>Within the directory you want your Plop script to live, run this command from the Terminal:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">npm</span> <span class="token function">install</span> --save-dev plop
</code></pre>
<p>This will generate 3 files:</p>
<ul>
<li>
<p>A node_modules folder for all your libraries, packages, and third-party code.</p>
</li>
<li>
<p>A package.json file. This will give you a starting point for your scripts</p>
</li>
<li>
<p>A package-lock.json file. This will lock down the versions for each package</p>
</li>
</ul>
<img alt="Folder Contents" loading="lazy" width="1346" height="784" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2FfolderContents.8b668cfa.png&amp;w=1920&amp;q=75 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2FfolderContents.8b668cfa.png&amp;w=3840&amp;q=75 2x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2FfolderContents.8b668cfa.png&amp;w=3840&amp;q=75">
<p>Inside, we can create a fourth file: <strong>plopfile.js.</strong> This is where we’ll define all of our generators, actions, and helpers.</p>
<p>A basic Plopfile is really… basic:</p>
<pre class="language-jsx"><code class="language-jsx"><span class="token keyword module">export</span> <span class="token keyword module">default</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">plop</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token comment">// create your generators here</span>
  plop<span class="token punctuation">.</span><span class="token method function property-access">setGenerator</span><span class="token punctuation">(</span><span class="token string">'basics'</span><span class="token punctuation">,</span> <span class="token punctuation">{</span>
    <span class="token literal-property property">description</span><span class="token operator">:</span> <span class="token string">'this is a skeleton plopfile'</span><span class="token punctuation">,</span>
    <span class="token literal-property property">prompts</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token comment">// array of inquirer prompts</span>
    <span class="token literal-property property">actions</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token comment">// array of actions</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
</code></pre>
<ul>
<li>
<p>We’ve established a node module that exports a function and accepts <code>plop</code> as a parameter.</p>
</li>
<li>
<p>Inside, we have a single generator called <code>basics</code></p>
</li>
<li>
<p>The <code>description</code> describes what the generator does</p>
</li>
<li>
<p><code>prompts</code> is an array of prompts. Each prompt is represented by a single object.</p>
</li>
<li>
<p><code>actions</code> take the information that the user provided from each prompt. <code>actions</code> is another array where each action is represented as another object.</p>
</li>
</ul>
<p>Let’s talk through the scripts that I wrote for the <a href="http://Compressed.fm">Compressed.fm</a> podcast, line-by-line. Between these 2 generators, this should cover 99% of your use cases.</p>
<p>Let’s start with our <code>episode</code> generator that stubs out all of our files:</p>
<pre class="language-jsx"><code class="language-jsx">plop<span class="token punctuation">.</span><span class="token method function property-access">setGenerator</span><span class="token punctuation">(</span><span class="token string">'episode'</span><span class="token punctuation">,</span> <span class="token punctuation">{</span>
  <span class="token literal-property property">description</span><span class="token operator">:</span> <span class="token string">'create a new episode'</span><span class="token punctuation">,</span>
  <span class="token literal-property property">prompts</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
</code></pre>
<p>First, let’s call our generator <code>episode</code> since we’re generating this folder structure for each new episode that we create. Then, I’ll provide a generic <code>description</code>: “create a new episode.”</p>
<p>Next, we want to ask the user 2 questions.</p>
<ol>
<li>
<p>What episode number is this?</p>
</li>
<li>
<p>Short name for the episode?</p>
</li>
</ol>
<p>Each prompt should be represented as its own object within the <code>prompts</code> array. There are several properties we’ll need to specify for each:</p>
<ul>
<li>
<p><code>type</code> - This is the <code>type</code> of prompt. PlopJS uses the <a href="https://github.com/SBoudrias/Inquirer.js/#question">InquirerJS library</a> to gather user data. You should refer to Inquirer’s documentation for the latest and greatest. Plus, there are several custom plugins that the community has created. But, here, I’ll cover a few of the highlights:</p>
<ul>
<li>
<p><code>input</code> - this is a string. For our use case, this is exactly what we need to get the short name of the episode</p>
</li>
<li>
<p><code>number</code> - this is a number (bet you didn’t see that one coming?!)</p>
</li>
<li>
<p><code>confirm</code> - this is a boolean. I’m sure you’ve seen this prompt before in other places: <code>(Y/n)</code></p>
</li>
<li>
<p><code>list</code>, <code>rawlist</code>, or <code>expand</code> - these are all string inputs, but you provide an array of choices within a separate property called <code>choices</code> You can also specify the <code>default</code> value and whether the user should be able to <code>loop</code> through the options (once they get to the bottom of the list, does it loop the user back to the top?) All three of these options behave similarly, but each of their displays is slightly different.</p>
</li>
<li>
<p><code>checkbox</code> - surprisingly enough, this is a string too. It also takes an array of <code>choices</code>. But, it allows the user to select multiple options, checking a specific option.</p>
</li>
<li>
<p><code>password</code> - this is a string, but the user’s input will be masked and replaced with <code>*</code></p>
</li>
<li>
<p><code>editor</code> - this is a string, but it accounts for a larger input by launching an instance of the user’s preferred editor on a temporary file.</p>
</li>
</ul>
</li>
<li>
<p><code>name</code> - this is a string used to store the user’s answers. You can think of this as the variable name.</p>
</li>
<li>
<p><code>message</code> - this is a string containing the question that you want to print to the Terminal.</p>
</li>
</ul>
<pre class="language-jsx"><code class="language-jsx">plop<span class="token punctuation">.</span><span class="token method function property-access">setGenerator</span><span class="token punctuation">(</span><span class="token string">'episode'</span><span class="token punctuation">,</span> <span class="token punctuation">{</span>
  <span class="token literal-property property">description</span><span class="token operator">:</span> <span class="token string">'create a new episode'</span><span class="token punctuation">,</span>
  <span class="token literal-property property">prompts</span><span class="token operator">:</span> <span class="token punctuation">[</span>
    <span class="token punctuation">{</span>
      <span class="token literal-property property">type</span><span class="token operator">:</span> <span class="token string">'number'</span><span class="token punctuation">,</span>
      <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">'episodeNumber'</span><span class="token punctuation">,</span>
      <span class="token literal-property property">message</span><span class="token operator">:</span> <span class="token string">'What episode number is this?'</span><span class="token punctuation">,</span>
    <span class="token punctuation">}</span><span class="token punctuation">,</span>
    <span class="token punctuation">{</span>
      <span class="token literal-property property">type</span><span class="token operator">:</span> <span class="token string">'input'</span><span class="token punctuation">,</span>
      <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">'episodeShortName'</span><span class="token punctuation">,</span>
      <span class="token literal-property property">message</span><span class="token operator">:</span> <span class="token string">'Short name for this episode?'</span><span class="token punctuation">,</span>
    <span class="token punctuation">}</span><span class="token punctuation">,</span>
  <span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
</code></pre>
<p>Lastly, we’ll need to specify an <code>actions</code> property. We need to do something with the information that the user provided. Just like the <code>prompt</code> property, this takes an array of actions. Each action is represented by a single object.</p>
<p>Several actions are <a href="https://plopjs.com/documentation/#built-in-actions">built in</a>. I’ll cover the highlights here, but you’ll need to refer to the documentation for more specifics.</p>
<ul>
<li><code>add</code> - this adds a file to our project. With this action, you’ll need to specify a template that will be used to create the file. It’s encouraged that within your project folder, you’ll have a plop-templates folder where all these templated files and folders live. The templates support the handlebar format so you can inject the user’s input into the template.</li>
</ul>
<blockquote>
<p>If you’re new to handlebars, have no fear! You only need to know a couple of things.</p>
<ol>
<li>Your file will end in <code>.hbs</code>, the handlebar extension</li>
<li>Inside your file, if you want to reference a variable, wrap it in double curly brackets: <code>{{ episodeNumber }}</code>. With handlebars, you can also specify modifiers to format the text you’re passing in. For example, by adding <code>titleCase</code> in front of my variable <code>name</code>, it will capitalize the first letter of each word: <code>{{ titleCase name }}</code></li>
</ol>
</blockquote>
<ul>
<li>
<p><code>addMany</code> - allows you to add multiple files to our project with a single action</p>
</li>
<li>
<p><code>modify</code> - you can use this action in one of two ways. You can either specify a <code>pattern</code> to find and replace the text in your file. Or, you can use a <code>transform</code> property to transform the file contents.</p>
</li>
<li>
<p><code>append</code> - this action allows you to append data in a file to a particular location</p>
</li>
<li>
<p>Custom Action - you can create your own, custom actions. PlopJS is just JavaScript, so anything you do in JavaScript, you can do it within Plop.</p>
</li>
</ul>
<p>For our episode folder generator, I created a custom action. <code>add</code> and <code>addMany</code> is built in, but it’s used specifically for working with files, <strong><em>not</em></strong> folders. Besides, I wanted to copy the entire directory structure instead of having to write a single action to generate each folder individually. That doesn’t sound easy or fun and would probably be difficult to maintain.</p>
<p>Instead of the <strong>plop-templates</strong> folder, I created a subfolder called <strong>episode</strong>. Inside I have my templated file and folder structure setup.</p>
<p>Now, let’s create a custom action. The Plop API gets us access to a <code>setActionType</code> method. The first parameter it takes is the name of the action that you want to create. The second parameter takes a function that does all the heavy lifting.</p>
<p>By default, the function takes 3 parameters:</p>
<ul>
<li>
<p><code>answers</code> - this is an object that contains answers to all the generator prompts</p>
</li>
<li>
<p><code>config</code> - this is the action configuration object from the actions array, set in your generator.</p>
</li>
<li>
<p><code>plop</code> - this is the plop API. <a href="https://plopjs.com/documentation/#other-methods">There are several methods you can use.</a></p>
</li>
</ul>
<p>The body of the function is where all the magic happens. ✨</p>
<pre class="language-jsx"><code class="language-jsx">plop<span class="token punctuation">.</span><span class="token method function property-access">setActionType</span><span class="token punctuation">(</span><span class="token string">'copyFolder'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token parameter">answers<span class="token punctuation">,</span> config<span class="token punctuation">,</span> plop</span><span class="token punctuation">)</span> <span class="token arrow operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token keyword">const</span> source <span class="token operator">=</span> plop<span class="token punctuation">.</span><span class="token method function property-access">renderString</span><span class="token punctuation">(</span>config<span class="token punctuation">.</span><span class="token property-access">source</span><span class="token punctuation">,</span> answers<span class="token punctuation">)</span>
  <span class="token keyword">const</span> target <span class="token operator">=</span> plop<span class="token punctuation">.</span><span class="token method function property-access">renderString</span><span class="token punctuation">(</span>config<span class="token punctuation">.</span><span class="token property-access">target</span><span class="token punctuation">,</span> answers<span class="token punctuation">)</span>

  fs<span class="token punctuation">.</span><span class="token method function property-access">mkdirSync</span><span class="token punctuation">(</span>target<span class="token punctuation">)</span>

  fs<span class="token punctuation">.</span><span class="token method function property-access">copy</span><span class="token punctuation">(</span>source<span class="token punctuation">,</span> target<span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token parameter">err</span><span class="token punctuation">)</span> <span class="token arrow operator">=&gt;</span> <span class="token punctuation">{</span>
    <span class="token keyword control-flow">if</span> <span class="token punctuation">(</span>err<span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token console class-name">console</span><span class="token punctuation">.</span><span class="token method function property-access">log</span><span class="token punctuation">(</span><span class="token string">'Whoops! We had an issue copying the folder'</span><span class="token punctuation">)</span>
      <span class="token keyword control-flow">return</span> <span class="token console class-name">console</span><span class="token punctuation">.</span><span class="token method function property-access">error</span><span class="token punctuation">(</span>err<span class="token punctuation">)</span>
    <span class="token punctuation">}</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
</code></pre>
<ul>
<li>
<p>In my custom action, I used the <code>renderString</code> method to get the <code>source</code> and <code>target</code> that I set inside the generator action:</p>
<pre class="language-jsx"><code class="language-jsx">plop<span class="token punctuation">.</span><span class="token method function property-access">setGenerator</span><span class="token punctuation">(</span><span class="token string">'episode'</span><span class="token punctuation">,</span> <span class="token punctuation">{</span>
  <span class="token spread operator">...</span>
  <span class="token literal-property property">actions</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token punctuation">{</span>
    <span class="token literal-property property">type</span><span class="token operator">:</span> <span class="token string">"copyFolder"</span><span class="token punctuation">,</span>
    <span class="token literal-property property">source</span><span class="token operator">:</span> <span class="token string">'./plop-templates/episode'</span><span class="token punctuation">,</span>
    <span class="token literal-property property">target</span><span class="token operator">:</span> <span class="token string">'{{leadingZeros episodeNumber}}__{{upperCase (dashCase episodeShortName)}}'</span><span class="token punctuation">,</span>
  <span class="token punctuation">}</span><span class="token punctuation">]</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre>
</li>
<li>
<p>Then, I use the <a href="https://nodejs.org/api/fs.html">Node file system API</a> to grab the source <code>fs.mkdirSync(target)</code> and make a copy <code>fs.copy()</code></p>
</li>
<li>
<p>If there’s a problem, then it will display an error in the console.</p>
</li>
</ul>
<p>Here’s the generator code, all together:</p>
<pre class="language-jsx"><code class="language-jsx">plop<span class="token punctuation">.</span><span class="token method function property-access">setGenerator</span><span class="token punctuation">(</span><span class="token string">'episode'</span><span class="token punctuation">,</span> <span class="token punctuation">{</span>
  <span class="token literal-property property">description</span><span class="token operator">:</span> <span class="token string">'create a new episode'</span><span class="token punctuation">,</span>
  <span class="token literal-property property">prompts</span><span class="token operator">:</span> <span class="token punctuation">[</span>
    <span class="token punctuation">{</span>
      <span class="token literal-property property">type</span><span class="token operator">:</span> <span class="token string">'number'</span><span class="token punctuation">,</span>
      <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">'episodeNumber'</span><span class="token punctuation">,</span>
      <span class="token literal-property property">message</span><span class="token operator">:</span> <span class="token string">'What episode number is this?'</span><span class="token punctuation">,</span>
    <span class="token punctuation">}</span><span class="token punctuation">,</span>
    <span class="token punctuation">{</span>
      <span class="token literal-property property">type</span><span class="token operator">:</span> <span class="token string">'input'</span><span class="token punctuation">,</span>
      <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">'episodeShortName'</span><span class="token punctuation">,</span>
      <span class="token literal-property property">message</span><span class="token operator">:</span> <span class="token string">'Short name for this episode?'</span><span class="token punctuation">,</span>
    <span class="token punctuation">}</span><span class="token punctuation">,</span>
  <span class="token punctuation">]</span><span class="token punctuation">,</span>
  <span class="token literal-property property">actions</span><span class="token operator">:</span> <span class="token punctuation">[</span>
    <span class="token punctuation">{</span>
      <span class="token literal-property property">type</span><span class="token operator">:</span> <span class="token string">'copyFolder'</span><span class="token punctuation">,</span>
      <span class="token literal-property property">source</span><span class="token operator">:</span> <span class="token string">'./plop-templates/episode'</span><span class="token punctuation">,</span>
      <span class="token literal-property property">target</span><span class="token operator">:</span>
        <span class="token string">'{{leadingZeros episodeNumber}}__{{upperCase (dashCase episodeShortName)}}'</span><span class="token punctuation">,</span>
    <span class="token punctuation">}</span><span class="token punctuation">,</span>
  <span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
</code></pre>
<p>You’ll notice that when I created the <code>target</code> file path, I used handlebars to make the target path dynamic.</p>
<p>If the user said that we’re on episode <code>123</code> and the short name of the episode is <code>Sami Jaber</code>, then the folder it creates is named <code>0123__SAMI-JABER</code>.</p>
<p>The double curly brackets are part of the handlebars templating language. The value on the far right is the variable — in this case, the <code>episodeNumber</code> and the <code>episodeShortName</code> that the user provided. Then, the value on the left is the modifier or helper.</p>
<p>Plop has several helpers that it providers out of the box. These are mostly case modifiers:</p>
<ul>
<li>
<p><code>camelCase</code> - changesTheFormatToThis</p>
</li>
<li>
<p><code>snakeCase</code> - changes_the_format_to_this</p>
</li>
<li>
<p><code>dashCase</code> or <code>kebabCase</code> - chages-the-case-to-this</p>
</li>
<li>
<p><code>dotCase</code> - <a href="http://changes.theformat.to">changes.theformat.to</a>.this</p>
</li>
<li>
<p><code>pathCase</code> - chages/the/case/to/this</p>
</li>
<li>
<p><code>properCase</code> or <code>pascalCase</code> - changesTheFormatToThis</p>
</li>
<li>
<p><code>lowerCase</code> - changes the format to this</p>
</li>
<li>
<p><code>sentenceCase</code> - Changes the format to this</p>
</li>
<li>
<p><code>constantCase</code> - CHANGES_THE_FORMAT_TO_THIS</p>
</li>
<li>
<p><code>titleCase</code> - Changes The Format to This</p>
</li>
</ul>
<p>Despite all these options, there’s not one for uppercase. So, we’ll need to roll up our sleeves and write a little bit of code:</p>
<pre class="language-jsx"><code class="language-jsx">plop<span class="token punctuation">.</span><span class="token method function property-access">setHelper</span><span class="token punctuation">(</span><span class="token string">'upperCase'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token parameter">txt</span><span class="token punctuation">)</span> <span class="token arrow operator">=&gt;</span> txt<span class="token punctuation">.</span><span class="token method function property-access">toUpperCase</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
</code></pre>
<p>As part of Plop’s API, you can use the <code>setHelper</code> method to create your own helper. The first parameter is a string specifying what you want to name your helper. The second parameter is a function. Here, you’re writing regular JavaScript code. I’m passing in the text that I want to modify and then returning the reformatted string.</p>
<p>JavaScript has a built method for converting strings to uppercase, so this helper is pretty simple. But, I also wrote a custom helper to add leading zeros.</p>
<p>This helper looks to see if the text being passed in is less than <code>10</code>, if it is, then it will return the string with three leading zeros. Otherwise, it will check to see if the number is less than <code>100</code>. If it is, then we only need two leading zeros. Lastly, it checks to see if the text is less than <code>1000</code>. If so, then only adds one <code>0</code>. If none of these conditions match, then it will return the string as is.</p>
<pre class="language-jsx"><code class="language-jsx">plop<span class="token punctuation">.</span><span class="token method function property-access">setHelper</span><span class="token punctuation">(</span><span class="token string">'leadingZeros'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token parameter">text</span><span class="token punctuation">)</span> <span class="token arrow operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token keyword control-flow">if</span> <span class="token punctuation">(</span>text <span class="token operator">&lt;</span> <span class="token number">10</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword control-flow">return</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">000</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>text<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span>
  <span class="token punctuation">}</span>

  <span class="token keyword control-flow">if</span> <span class="token punctuation">(</span>text <span class="token operator">&lt;</span> <span class="token number">100</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword control-flow">return</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">00</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>text<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span>
  <span class="token punctuation">}</span>

  <span class="token keyword control-flow">if</span> <span class="token punctuation">(</span>text <span class="token operator">&lt;</span> <span class="token number">1000</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword control-flow">return</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">0</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>text<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span>
  <span class="token punctuation">}</span>

  <span class="token keyword control-flow">return</span> text
<span class="token punctuation">}</span><span class="token punctuation">)</span>
</code></pre>
<p>Awesome!</p>
<p>Now, let’s look at the show notes generator. There are a few key differences. Here’s the entire generator, but we’ll break it down line by line, below.</p>
<pre class="language-jsx"><code class="language-jsx">plop<span class="token punctuation">.</span><span class="token method function property-access">setGenerator</span><span class="token punctuation">(</span><span class="token string">'notes'</span><span class="token punctuation">,</span> <span class="token punctuation">{</span>
  <span class="token literal-property property">description</span><span class="token operator">:</span> <span class="token string">'creates the show notes'</span><span class="token punctuation">,</span>
  <span class="token literal-property property">prompts</span><span class="token operator">:</span> <span class="token punctuation">[</span>
    <span class="token punctuation">{</span>
      <span class="token literal-property property">type</span><span class="token operator">:</span> <span class="token string">'list'</span><span class="token punctuation">,</span>
      <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">'whichEpisode'</span><span class="token punctuation">,</span>
      <span class="token literal-property property">message</span><span class="token operator">:</span> <span class="token string">'Which episode do you want to add notes to?'</span><span class="token punctuation">,</span>
      <span class="token function-variable function">choices</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token arrow operator">=&gt;</span> <span class="token punctuation">{</span>
        <span class="token comment">// get all the directories within the current folder</span>
        <span class="token keyword control-flow">return</span> <span class="token function">readdirSync</span><span class="token punctuation">(</span><span class="token string">'.'</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> <span class="token literal-property property">withFileTypes</span><span class="token operator">:</span> <span class="token boolean">true</span> <span class="token punctuation">}</span><span class="token punctuation">)</span>
          <span class="token punctuation">.</span><span class="token method function property-access">filter</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">dirent</span><span class="token punctuation">)</span> <span class="token arrow operator">=&gt;</span> dirent<span class="token punctuation">.</span><span class="token method function property-access">isDirectory</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
          <span class="token punctuation">.</span><span class="token method function property-access">map</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">dirent</span><span class="token punctuation">)</span> <span class="token arrow operator">=&gt;</span> dirent<span class="token punctuation">.</span><span class="token property-access">name</span><span class="token punctuation">)</span>
      <span class="token punctuation">}</span><span class="token punctuation">,</span>
    <span class="token punctuation">}</span><span class="token punctuation">,</span>
    <span class="token punctuation">{</span>
      <span class="token literal-property property">type</span><span class="token operator">:</span> <span class="token string">'checkbox'</span><span class="token punctuation">,</span>
      <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">'sponsors'</span><span class="token punctuation">,</span>
      <span class="token literal-property property">message</span><span class="token operator">:</span> <span class="token string">'Which sponsors do you have for this episode?'</span><span class="token punctuation">,</span>
      <span class="token literal-property property">choices</span><span class="token operator">:</span> <span class="token punctuation">[</span>
        <span class="token punctuation">{</span> <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">'ZEAL'</span><span class="token punctuation">,</span> <span class="token literal-property property">value</span><span class="token operator">:</span> <span class="token string">'zeal.md'</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
        <span class="token punctuation">{</span> <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">'Vercel'</span><span class="token punctuation">,</span> <span class="token literal-property property">value</span><span class="token operator">:</span> <span class="token string">'vercel.md'</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
        <span class="token punctuation">{</span> <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">'DatoCMS'</span><span class="token punctuation">,</span> <span class="token literal-property property">value</span><span class="token operator">:</span> <span class="token string">'datocms.md'</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
        <span class="token punctuation">{</span> <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">'Daily.dev'</span><span class="token punctuation">,</span> <span class="token literal-property property">value</span><span class="token operator">:</span> <span class="token string">'dailydev.md'</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
        <span class="token punctuation">{</span> <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">'Hashnode'</span><span class="token punctuation">,</span> <span class="token literal-property property">value</span><span class="token operator">:</span> <span class="token string">'hashnode.md'</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
      <span class="token punctuation">]</span><span class="token punctuation">,</span>
    <span class="token punctuation">}</span><span class="token punctuation">,</span>
  <span class="token punctuation">]</span><span class="token punctuation">,</span>
  <span class="token function-variable function">actions</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span> sponsors<span class="token punctuation">,</span> whichEpisode <span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token arrow operator">=&gt;</span> <span class="token punctuation">{</span>
    <span class="token keyword">let</span> actions <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span>

    <span class="token comment">// create the file based on the template</span>
    actions<span class="token punctuation">.</span><span class="token method function property-access">push</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
      <span class="token literal-property property">type</span><span class="token operator">:</span> <span class="token string">'add'</span><span class="token punctuation">,</span>
      <span class="token literal-property property">path</span><span class="token operator">:</span> <span class="token string">'{{whichEpisode}}/Episode {{epNumber whichEpisode}} - Show Notes.txt'</span><span class="token punctuation">,</span>
      <span class="token literal-property property">templateFile</span><span class="token operator">:</span> <span class="token string">'plop-templates/show-notes.md.hbs'</span><span class="token punctuation">,</span>
      <span class="token literal-property property">abortOnFail</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
      <span class="token literal-property property">skipIfExists</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span>

    <span class="token comment">// append all the sponsor information to the file</span>
    sponsors<span class="token punctuation">.</span><span class="token method function property-access">map</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">sponsor</span><span class="token punctuation">)</span> <span class="token arrow operator">=&gt;</span> <span class="token punctuation">{</span>
      <span class="token keyword">const</span> sponsorFile <span class="token operator">=</span> sponsor
      actions<span class="token punctuation">.</span><span class="token method function property-access">push</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
        <span class="token literal-property property">type</span><span class="token operator">:</span> <span class="token string">'append'</span><span class="token punctuation">,</span>
        <span class="token literal-property property">path</span><span class="token operator">:</span> <span class="token string">'{{whichEpisode}}/Episode {{epNumber whichEpisode}} - Show Notes.txt'</span><span class="token punctuation">,</span>
        <span class="token literal-property property">templateFile</span><span class="token operator">:</span> <span class="token string">'plop-templates/sponsors/'</span> <span class="token operator">+</span> sponsorFile<span class="token punctuation">,</span>
        <span class="token literal-property property">abortOnFail</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
      <span class="token punctuation">}</span><span class="token punctuation">)</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span>

    actions<span class="token punctuation">.</span><span class="token method function property-access">push</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
      <span class="token literal-property property">type</span><span class="token operator">:</span> <span class="token string">'append'</span><span class="token punctuation">,</span>
      <span class="token literal-property property">path</span><span class="token operator">:</span> <span class="token string">'{{whichEpisode}}/Episode {{epNumber whichEpisode}} - Show Notes.txt'</span><span class="token punctuation">,</span>
      <span class="token literal-property property">template</span><span class="token operator">:</span> <span class="token string">'## Show Notes'</span><span class="token punctuation">,</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span>

    <span class="token keyword control-flow">return</span> actions
  <span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
</code></pre>
<p>With the first prompt, I’m using Node’s <code>readdirSync()</code> method to read all the files within the current folder (<code>.</code>)</p>
<pre class="language-jsx"><code class="language-jsx"><span class="token punctuation">{</span>
    <span class="token literal-property property">type</span><span class="token operator">:</span> <span class="token string">'list'</span><span class="token punctuation">,</span>
    <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">'whichEpisode'</span><span class="token punctuation">,</span>
    <span class="token literal-property property">message</span><span class="token operator">:</span> <span class="token string">'Which episode do you want to add notes to?'</span><span class="token punctuation">,</span>
    <span class="token function-variable function">choices</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token arrow operator">=&gt;</span> <span class="token punctuation">{</span>
      <span class="token comment">// get all the directories within the current folder</span>
      <span class="token keyword control-flow">return</span> <span class="token function">readdirSync</span><span class="token punctuation">(</span><span class="token string">'.'</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> <span class="token literal-property property">withFileTypes</span><span class="token operator">:</span> <span class="token boolean">true</span> <span class="token punctuation">}</span><span class="token punctuation">)</span>
        <span class="token punctuation">.</span><span class="token method function property-access">filter</span><span class="token punctuation">(</span><span class="token parameter">dirent</span> <span class="token arrow operator">=&gt;</span> dirent<span class="token punctuation">.</span><span class="token method function property-access">isDirectory</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
        <span class="token punctuation">.</span><span class="token method function property-access">map</span><span class="token punctuation">(</span><span class="token parameter">dirent</span> <span class="token arrow operator">=&gt;</span> dirent<span class="token punctuation">.</span><span class="token property-access">name</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span><span class="token punctuation">,</span>
  <span class="token punctuation">}</span>
</code></pre>
<ul>
<li>
<p>Then, I’m filtering the result to only display the contents that are folders or directories</p>
</li>
<li>
<p>Then, I loop over each with a <code>map</code> function to display the names of the directories. This is used to populate the <code>choices</code> property.</p>
</li>
<li>
<p><code>choices</code> is supposed to take an array, but here, I’m giving a function that <strong><em>returns</em></strong> an array. 😎</p>
</li>
</ul>
<p>For the Sponsor prompt, I hard coded in an array of options where the <code>name</code> is what gets displayed in the Terminal and the value corresponds to a markdown file inside my plop-templates folder.</p>
<pre class="language-jsx"><code class="language-jsx"><span class="token punctuation">{</span>
    <span class="token literal-property property">type</span><span class="token operator">:</span> <span class="token string">'checkbox'</span><span class="token punctuation">,</span>
    <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">'sponsors'</span><span class="token punctuation">,</span>
    <span class="token literal-property property">message</span><span class="token operator">:</span> <span class="token string">'Which sponsors do you have for this episode?'</span><span class="token punctuation">,</span>
    <span class="token literal-property property">choices</span><span class="token operator">:</span> <span class="token punctuation">[</span>
      <span class="token punctuation">{</span> <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">'ZEAL'</span><span class="token punctuation">,</span> <span class="token literal-property property">value</span><span class="token operator">:</span> <span class="token string">'zeal.md'</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
      <span class="token punctuation">{</span> <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">'Vercel'</span><span class="token punctuation">,</span> <span class="token literal-property property">value</span><span class="token operator">:</span> <span class="token string">'vercel.md'</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
      <span class="token punctuation">{</span> <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">'DatoCMS'</span><span class="token punctuation">,</span> <span class="token literal-property property">value</span><span class="token operator">:</span> <span class="token string">'datocms.md'</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
      <span class="token punctuation">{</span> <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">'Daily.dev'</span><span class="token punctuation">,</span> <span class="token literal-property property">value</span><span class="token operator">:</span> <span class="token string">'dailydev.md'</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
      <span class="token punctuation">{</span> <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">'Hashnode'</span><span class="token punctuation">,</span> <span class="token literal-property property">value</span><span class="token operator">:</span> <span class="token string">'hashnode.md'</span> <span class="token punctuation">}</span>
    <span class="token punctuation">]</span>
  <span class="token punctuation">}</span>
</code></pre>
<ul>
<li>The <code>type: checkbox</code> allows me to select as many sponsors as I want. This is perfect because some episodes have 1 sponsor, while others have 3.</li>
</ul>
<p>For our <code>actions</code> object, this needs an array. Instead, I’m giving it a function but, it <strong><em>returns</em></strong> our array. The cool part about this: it allows me to process the sponsors, loop over them, and push each action to an <code>actions</code> array that gets returned at the bottom of the function. This is particularly helpful because I don’t know how many action steps I have. It all depends on how many sponsors the user selects.</p>
<pre class="language-jsx"><code class="language-jsx"><span class="token function-variable function">actions</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span> sponsors<span class="token punctuation">,</span> whichEpisode <span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token arrow operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token keyword">let</span> actions <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span>

  <span class="token comment">// create the file based on the template</span>
  actions<span class="token punctuation">.</span><span class="token method function property-access">push</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
    <span class="token literal-property property">type</span><span class="token operator">:</span> <span class="token string">'add'</span><span class="token punctuation">,</span>
    <span class="token literal-property property">path</span><span class="token operator">:</span> <span class="token string">'{{whichEpisode}}/Episode {{epNumber whichEpisode}} - Show Notes.txt'</span><span class="token punctuation">,</span>
    <span class="token literal-property property">templateFile</span><span class="token operator">:</span> <span class="token string">'plop-templates/show-notes.md.hbs'</span><span class="token punctuation">,</span>
    <span class="token literal-property property">abortOnFail</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
    <span class="token literal-property property">skipIfExists</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span>

  <span class="token comment">// append all the sponsor information to the file</span>
  sponsors<span class="token punctuation">.</span><span class="token method function property-access">map</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">sponsor</span><span class="token punctuation">)</span> <span class="token arrow operator">=&gt;</span> <span class="token punctuation">{</span>
    <span class="token keyword">const</span> sponsorFile <span class="token operator">=</span> sponsor
    actions<span class="token punctuation">.</span><span class="token method function property-access">push</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
      <span class="token literal-property property">type</span><span class="token operator">:</span> <span class="token string">'append'</span><span class="token punctuation">,</span>
      <span class="token literal-property property">path</span><span class="token operator">:</span> <span class="token string">'{{whichEpisode}}/Episode {{epNumber whichEpisode}} - Show Notes.txt'</span><span class="token punctuation">,</span>
      <span class="token literal-property property">templateFile</span><span class="token operator">:</span> <span class="token string">'plop-templates/sponsors/'</span> <span class="token operator">+</span> sponsorFile<span class="token punctuation">,</span>
      <span class="token literal-property property">abortOnFail</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span>

  actions<span class="token punctuation">.</span><span class="token method function property-access">push</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
    <span class="token literal-property property">type</span><span class="token operator">:</span> <span class="token string">'append'</span><span class="token punctuation">,</span>
    <span class="token literal-property property">path</span><span class="token operator">:</span> <span class="token string">'{{whichEpisode}}/Episode {{epNumber whichEpisode}} - Show Notes.txt'</span><span class="token punctuation">,</span>
    <span class="token literal-property property">template</span><span class="token operator">:</span> <span class="token string">'## Show Notes'</span><span class="token punctuation">,</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span>

  <span class="token keyword control-flow">return</span> actions
<span class="token punctuation">}</span>
</code></pre>
<ul>
<li>
<p>The first <code>action</code> that I add to the array, uses the standard <code>add</code> event that <strong>adds</strong> an event to my folder.</p>
</li>
<li>
<p>Then, the remaining actions <code>append</code> (another standard Plop event) the content of my files to the newly created file.</p>
</li>
</ul>
<hr>
<h2>Triggering our Scripts</h2>
<p>Now, that we’ve written these scripts, let’s trigger them from the command line.</p>
<p>Within our package.json file, inside the <code>scripts</code> block, I’m going to add a script for each generator:</p>
<pre class="language-jsx"><code class="language-jsx"><span class="token string-property property">"scripts"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
  <span class="token string-property property">"new:episode"</span><span class="token operator">:</span> <span class="token string">"plop episode"</span><span class="token punctuation">,</span>
  <span class="token string-property property">"new:notes"</span><span class="token operator">:</span> <span class="token string">"plop notes"</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
</code></pre>
<ul>
<li>The command <code>plop</code> will trigger <code>plop</code>. Then, here, <code>episode</code> and <code>notes</code> are the names of the generators that I created.</li>
</ul>
<p>Now, I can run <code>npm new:episode</code> in the Terminal, which will trigger our <code>episode</code> generator and <code>npm new:notes</code> will trigger our <code>notes</code> generator.</p>
<p>Of course, you could also simply run <code>plop episode</code> and <code>plop notes</code> within the Terminal, but I find using the <code>scripts</code> section of the package.json file a little easier. 🤷‍♀️</p>
<hr>
<h2>Power Tip: Create aliases for your PlopJS Scripts and Trigger them from Alfred</h2>
<p>For the scripts that I use the most, I’ve created aliases within my Oh My Zsh config file:</p>
<pre class="language-jsx"><code class="language-jsx"># <span class="token maybe-class-name">Compressed</span><span class="token punctuation">.</span><span class="token property-access">fm</span>
# <span class="token maybe-class-name">This</span> generates a <span class="token keyword">new</span> <span class="token class-name">project</span> structure <span class="token keyword control-flow">for</span> an episode
alias cfm<span class="token operator">:</span>episode<span class="token operator">=</span><span class="token string">"npm --prefix /Users/amydutton/COMPRESSED-FM/EPISODES run new:episode"</span>
# <span class="token maybe-class-name">This</span> generates a <span class="token keyword">new</span> <span class="token class-name">project</span> structure <span class="token keyword control-flow">for</span> a live episode
alias cfm<span class="token operator">:</span>live<span class="token operator">=</span><span class="token string">"npm --prefix /Users/amydutton/COMPRESSED-FM/EPISODES run new:live"</span>
# <span class="token maybe-class-name">This</span> generates a text file <span class="token keyword">with</span> the sponsor information included
alias cfm<span class="token operator">:</span>showNotes<span class="token operator">=</span><span class="token string">"npm --prefix /Users/amydutton/COMPRESSED-FM/EPISODES run new:notes"</span>

# <span class="token maybe-class-name">Everything</span> <span class="token maybe-class-name">Svelte</span> <span class="token maybe-class-name">Lesson</span>
# <span class="token maybe-class-name">This</span> generates a <span class="token keyword">new</span> <span class="token class-name">project</span> structure <span class="token keyword control-flow">for</span> a lesson
alias es<span class="token operator">:</span>lesson<span class="token operator">=</span><span class="token string">"npm --prefix /Users/amydutton/EVERYTHING-SVELTE/COURSE run new:lesson"</span>
alias es<span class="token operator">:</span>update<span class="token operator">=</span><span class="token string">"npm --prefix /Users/amydutton/EVERYTHING-SVELTE/COURSE run update:lesson"</span>
alias es<span class="token operator">:</span>appendix<span class="token operator">=</span><span class="token string">"npm --prefix /Users/amydutton/EVERYTHING-SVELTE/COURSE run new:appendix"</span>
</code></pre>
<p>I’m also a big fan of <a href="https://www.alfredapp.com/">Alfred</a>. The easiest way to describe Alfred is to call it an app launcher or Spotlight replacement. However, it does <em>so much more</em> than that. For example, I can invoke these commands from Alfred.</p>
<iframe title="vimeo-player" src="https://player.vimeo.com/video/804080877?h=cc94906f0f" width="640" height="360" frameborder="0"></iframe>
<p>Hopefully, these examples will get your creative juices flowing for various use cases. If you’re interested in exploring more and all that Plop has to offer, I’d recommend starting with the <a href="https://plopjs.com/">Plop documentation</a>.</p>
<p>There’s also a <s>fantastic</s> awesome repository on GitHub called <a href="https://github.com/plopjs/awesome-plop">awesome-plop</a> that highlights various actions, helpers, and generators that the community has created.</p>
<p>Within this post, I also mentioned several workflows that I’m using to manage the <a href="http://Compressed.fm">Compressed.fm</a> podcast. I’ve <a href="https://github.com/ahaywood/plop-compressed-fm">open-sourced those files</a>. You’re more than welcome to fork that project, using it as a starting point.</p>]]></content:encoded>
            <author>spencer@planetaria.tech (Spencer Sharp)</author>
        </item>
    </channel>
</rss>