Creating your next custom boilerplate easily with Simple-Scaffold
Tags: tooling, generator, javascript, typescript
Published: 2022-08-10 08:04:00

As most people who have been working a lot in front-end development, I will acknowledge that no matter what framework, library set or tools you work with, you end up replicating a lot of files, especially with modern component-based frameworks, such as React, Angular and Vue.js.

They usually grow to be very different, but they almost always start with the same some sort of base-skeleton, which gets _built-upon_ later. A lot of pipeline code written, and **time wasted**.

## What have we got?

There are some existing solutions to these problems, but they usually are not very flexible, or are tied to one library or implementation. Let's take a look at a few examples.

- _IDE snippets_, such as VS Code's easy snippet extensions/configurations are very helpful. Basically, you can either use configuration JSON files to add snippets to your code quickly, or use extensions that simply fill these up for you.

  There are nice benefits to this, as you can start typing the 'prefix' of the snippet and hit <kbd>Tab</kbd> quickly to add small snippets of code. You can also predefine stopping points, which you can navigate using <kbd>Tab</kbd> after inserting the snippet, and insert arguments, method names, etc.

  These are nice, but only for small snippets. When you have to create an entire component, made by multiple files, you're stuck with the option to create multiple files yourself, name them appropriately, and paste different snippets on each file.

- _Some NPM libraries_ provide ways to scaffold files based on templates, but from what I've seen

  - they are tied to specific task runners, libraries or frameworks
  - they are difficult to set up and customize
  - combining your entire stack will be difficult

## Enter: Simple Scaffold

[Read the Simple Scaffold documentation][simple-scaffold]

I was frustrated with this, and all I wanted was to have different sets of files I could generate, simply by copying them at predefined directory structures, and fill them with some variable data.

For instance: I create a lot of React components at work. We have recently moved to a single-file component structure for starting components; but we have different types of components that we want to generate based on context, like different file contents for page containers versus general-use components.

Being fed up with my options, I made this small NPM package, that does just this, in a very easy and quick way to setup. Simply put your files wherever you want, and either use the CLI tool or the package as an import - and you're good to go. It **preserves your input directory/file structure**, replacing the handlebar tokens inside directory/file names and file contents.

## Simple Example: Jekyll Posts

Creating Jekyll posts is very simple! However I still had a little bit missing - I have to manually add the date every time? Oh, man. What about the file identifier name, and title? My `excerpt_separator`?

So I just made the simplest script in Ruby (to fit in with the Jekyll theme) to run the Simple Scaffold CLI with some custom parameters.

```ruby
#!/usr/bin/env ruby
require "json"

SCAFFOLD_DIR = "scaffold_templates"
*name = ARGV

data = { dateFmt: "yyyy-MM-dd", fullDateFmt: "yyyy-MM-dd HH:mm:ss XX" }

puts `
  npx simple-scaffold@latest "#{name.join(" ")}" \
    --templates #{SCAFFOLD_DIR}/**/* \
    --output _drafts \
    --overwrite true \
    --create-sub-folder false \
    --data '#{JSON.dump(data)}'
`
puts 'Done'
```

Let's run by this real quick:

- _Lines 1-7_ - setting up locals such as template directory base, and some variables to pass to the
  templates.
- _Lines 9-16_ - We pass the parameters via a shell call (back-ticks in ruby), and immediately run
  using npx.

Locals are passed to [Handlebars][handlebars], and can be used both in file/directory names and file contents.

Now all I had to do is create a place for my templates, and add a template inside:

```text
- scaffold_templates/
  -- {{now dateFmt}}-{{kebabCase name}}.md
```

And fill it up with some basic post:

```yaml
---
layout: post
title: "{{ startCase name }}"
date: {{ now fullDateFmt }}
excerpt_separator: <!-- more -->
categories:
---
Post content goes here
```

And voila! Running the script, along with a name:

```shell
./scaffold.rb "Billy Jean is not my lover"
```

Generates the following file structure:

```text
- _posts/
  -- 2019-03-06-billy-jean-is-not-my-lover.markdown
```

```yaml
---
layout: post
title: 'Billy Jean is not my lover'
date: 2019-03-06 16:43:38 +0200
excerpt_separator: <!-- more -->
categories:
---
Post content goes here
```

## You can do more

Take the same approach and think what you could create generators for with 0 effort. React or Vue components? You can bundle each in a folder with all the imports, exports, base props, etc all lined up with your component name.

Add a script to your `package.json`, a couple of files, and you're good to go:

**package.json**

```json
{
"gen:component": "npx simple-scaffold@latest -t scaffold_templates/react-component -o src/components -s true -w true '{\"className\": \"myClassName\",\"author\": \"Chen Asraf\"}'"
}
```

**`scaffold_templates/react-component/{{pascalCase name}}.tsx`**

```tsx
/**
 * Author: {{ author }}
 * Date: {{ now "yyyy-MM-dd" }}
 */
import React from 'react'
import { ComponentProps } from 'utils/types'

export interface {{pascalCase name}}Props extends ComponentProps {
  className?: string
}

export default {{camelCase name}}: React.FC<{{pascalCase name}}Props> = (props) => {
  return (
    <div className="{{className}}">{{camelCase name}} Component</div>
  )
}
```

**`scaffold_templates/react-component/index.ts`**

```tsx
export * from './{{pascalCase name}}'
```

Running `npm run gen:component MyComponent` will quickly generate your new components:

**`src/components/MyComponent/MyComponent.tsx`**

```tsx
/**
 * Author: Chen Asraf
 * Date: 2022-08-10
 */
import React from 'react'
import { ComponentProps } from 'utils/types'

export interface MyComponentProps extends ComponentProps {
  className?: string
}

export default MyComponent: React.FC<MyComponentProps> = (props) => {
  return (
    <div className="myClassName">MyComponent Component</div>
  )
}
```

**`src/components/MyComponent/index.ts`**

```tsx
export * from './MyComponent'
```

Why stop there? **Create entire app boilerplates** using the same method - create your boilerplate app, add all your libraries, and replace your app name with `{{name}}`. That's it! You can run this package with input from any local files and output them in (or as) your next project.

Check out the documentation by clicking [this link][simple-scaffold]!

[handlebars]: https://handlebarsjs.com
[simple-scaffold]: https://casraf.dev/simple-scaffold