Custom Developer Tooling

Element Three
TL;DR: I created a code generation CLI tool as a part of the React UI library/architecture that I designed and built for Element Three's Jamstack website production. It automates scaffolding components to save developers time and ensure standards and conventions are followed.

Overview

Problem

As our development team grew, we began spending a lot of time managing PR's, trying to document patterns/standards, etc—however, we weren't critiquing code for quality, but for consistency. We had a lot of hands in the cookie jar, and everyone has their own learned habits for things like how they'd like to format a component and where to put the files, and so on.

This pattern was resulting in wasted development time due to preventable mistakes which lead to frustrating code reviews, and most importantly, to wasted money.

Solution

As a solution, I created a CLI tool in order to maintain consistency and automate the tedious parts of component creation. The tool creates a component in the appropriate directory using provided template files, sets up the named exports, and exports the files from source directories, in order for absolute imports to work throughout the project.

Several hundred components throughout numerous projects have been created using this tool, saving a lot of time for developers, ensuring consistency, and making the right thing the easy thing.

Mini Demo

Code Sample

The repository for this code lives in a private repo that I no longer have access to—but below is a snippet of code, demonstrating the core functionality of the tool at an early stage, proof of concept development.

1const {Command} = require('@oclif/command')
2const fs = require('fs')
3const {cli} = require('cli-ux')
4const {pascalCase} = require('change-case')
5const {componentTemplate} = require('../templates/component')
6
7class MakeCommand extends Command {
8  async run() {
9    const {args} = this.parse(MakeCommand)
10    const type = args.type
11    const name = pascalCase(args.name)
12    const folderName = `./src/components/${type}s/${name}`
13
14    try {
15      cli.action.start('Making your component')
16      await cli.wait(50)
17      if (!fs.existsSync(folderName)) {
18        fs.mkdirSync(folderName)
19      }
20      if (!fs.existsSync(`${folderName}/${name}`)) {
21        fs.writeFile(
22          `${folderName}/${name}.jsx`,
23          componentTemplate(type, name),
24          'utf8',
25          (err) => {
26            if (err) throw err
27          }
28        )
29        fs.writeFile(
30          `${folderName}/index.js`,
31          `export * from "./${name}"`,
32          'utf8',
33          (err) => {
34            if (err) throw err
35          }
36        )
37        cli.action.stop('✅')
38
39        cli.action.start(`Exporting your component from ${type}s index file`)
40        await cli.wait(50)
41        fs.appendFile(
42          `./src/components/${type}s/index.js`,
43          `export * from "./${name}"\n`,
44          (err) => {
45            if (err) throw err
46          }
47        )
48        cli.action.stop('✅')
49      }
50    } catch (error) {
51      this.log(error)
52    }
53  }
54}
55
56MakeCommand.description = `Creates new components for neon projects
57
58With this command, you can create a new component in the appropriate directory.
59`
60
61MakeCommand.args = [
62  {
63    name: 'type',
64    required: true,
65    description: 'The type of component.',
66    default: 'layout',
67  },
68  {
69    name: 'name',
70    required: true,
71    description: 'The name of component.',
72    default: 'NewFile',
73  },
74]
75
76module.exports = MakeCommand
77

contact meviaemail

jared@jared.to

© 2021 Jared BrownIndianapolis, IN