DEV Community

Chen Asraf
Chen Asraf

Posted on • Updated on • Originally published at casraf.dev

Creating your next custom boilerplate easily with Simple-Scaffold

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 Tab quickly to add small snippets of code. You can also predefine stopping points, which you can navigate using Tab 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

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.

#!/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'
Enter fullscreen mode Exit fullscreen mode

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, 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:

- scaffold_templates/
  -- {{now dateFmt}}-{{kebabCase name}}.md
Enter fullscreen mode Exit fullscreen mode

And fill it up with some basic post:

---
layout: post
title: "{{ startCase name }}"
date: {{ now fullDateFmt }}
excerpt_separator: <!-- more -->
categories:
---
Post content goes here
Enter fullscreen mode Exit fullscreen mode

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

./scaffold.rb "Billy Jean is not my lover"
Enter fullscreen mode Exit fullscreen mode

Generates the following file structure:

- _posts/
  -- 2019-03-06-billy-jean-is-not-my-lover.markdown
Enter fullscreen mode Exit fullscreen mode
---
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
Enter fullscreen mode Exit fullscreen mode

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

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

scaffold_templates/react-component/{{pascalCase name}}.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>
  )
}
Enter fullscreen mode Exit fullscreen mode

scaffold_templates/react-component/index.ts

export * from './{{pascalCase name}}'
Enter fullscreen mode Exit fullscreen mode

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

src/components/MyComponent/MyComponent.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>
  )
}
Enter fullscreen mode Exit fullscreen mode

src/components/MyComponent/index.ts

export * from './MyComponent'
Enter fullscreen mode Exit fullscreen mode

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!

Top comments (0)