DEV Community

Andrei Neculaesei
Andrei Neculaesei

Posted on

Generating React Components with Auto

Introduction

Creating new components is something we have to do frequently in front-end projects.

Each component needs a directory, index file, component file, stories, and tests.

Manually creating these files is time-consuming and introduces the most dangerous enemy of productivity: friction.

In this post, we will look at how we can use Auto, my side-project, to automate creating new components in a React project.

Auto generating a component


What is Auto?

Auto is a command-line automation tool I've been working on in my spare time that allows you to write your scripts using TypeScript.

You can install it globally and locally, and it supports both global and project-local script repositories.

Auto provides a lot of useful abstractions and helpers that make it easy to write context-aware scripts that interact with the filesystem, execute shell commands, and more.

Auto scripts

Auto scripts are TypeScript files that export the result of a call to auto, a global function that takes a script definition object as its only argument.

This object contains the script's metadata, as well as its implementation, and it looks like this:

import "auto";

export default auto({
  id: "my-script",
  title: "\"my script\","
  params: {
    myparam: {
      title: "\"component name\","
      type: "string", // "string" | "number" | "boolean"
      required: true,
      // defaultvalue: ({ project, params }) => string|undefined
    },
  },
  // isvalid: (project) => project.hasdependency("something"),
  run: async ({ cwd, project, params, files, self, t, files, filemap }) => {
    //          ^ contextual variables and helpers
    // instructions
  },
});
Enter fullscreen mode Exit fullscreen mode

Setting up a component generator

Install Auto in your project

To install Auto, add it as a dev dependency to your project:

npm install -D @andrei.fyi/auto
Enter fullscreen mode Exit fullscreen mode

This is a quick overview of the commands that Auto provides:

  • auto ls - list all available scripts
  • auto run <script-id> - run a script
  • auto repl - start a REPL

You can alias auto run to npm run gen or yarn gen in package.json to make it easier to run your scripts.

Create a script repository

Auto looks for a "auto" or ".auto" directory in the root of your project.
There's no fixed structure that you need to follow; Auto will detect all the valid script files and ignore the others.

Because Auto works by injecting globals into your script, it will prompt you to auto-generate a tsconfig.json file inside your script repositories,
so that you get full type support when writing your scripts.

# create a local script repository at the root of your project
mkdir auto

# run `auto ls` and allow Auto to generate the tsconfig.json file
npx auto ls
Info: Using local repository: ~/lab/my-react-project/auto
Warning: Cannot find ~/lab/my-react-project/auto/tsconfig.json
? Do you want me to set it up? (Y/n)
Enter fullscreen mode Exit fullscreen mode

Create a component generator

Now that we have Auto installed and configured, we can start writing our script.

Let's first define what we want to achieve:

  • Run a command that prompts us for the name and destination of the component.
  • Create a series of files and directories based on templates.
  • Derive the path and content of each file from the component name and template.

Auto provides several helpers and abstractions that will help us achieve all these goals:

  • params - a map of parameters the user will be prompted for, and that will be passed to the script when it runs
  • project - a representation of the current project that includes a helper for writing files
  • t - a templating function that replaces __key__ placeholders with the provided values
  • files - an array that contains all the other files in the same directory as the script

Define a template for our component

Let's start by making a directory for our script and creating the template files.

Create the script directory:

mkdir auto/component
Enter fullscreen mode Exit fullscreen mode

Create the component file template:

// auto/component/__name__.tsx
export type __name__Props = {
};

export const __name__ = ({ }: __name__Props) => {
  return <div>__name__</div>;
};
Enter fullscreen mode Exit fullscreen mode

Create the test template:

// auto/component/__name__.test.tsx
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';

import { __name__ } from "./__name__";

describe("__name__", () => {
  beforeEach(() => {
    jest.clearAllMocks();
  });

  it("renders", () => {
    render(<__name__/>);
  })
});
Enter fullscreen mode Exit fullscreen mode

Create the story template:

// auto/component/__name__.stories.tsx
import type { StoryObj, Meta } from "@storybook/react";
import { __name__, __name__Props } from "./__name__";

const meta: Meta<typeof __name__> = {
  title: "__name__",
  component: __name__,
};
export default meta;

type Story = StoryObj<__name__Props>;

export const Default: Story = {
  args: {},
};
Enter fullscreen mode Exit fullscreen mode

Create the index file template:

// auto/component/index.ts
export * from "./__name__";
Enter fullscreen mode Exit fullscreen mode

Create the generator script

Now that we have our template files, we can start writing the script to generate components.

You can name the script file however you want, but it's a good practice to reference the script id in the file name.

// auto/component/auto-component.ts
import "auto";

export default auto({
  id: "component",
  title: "Generate a component",
  params: {
    name: {
      title: "Component Name",
      type: "string",
      required: true,
    },
    path: {
      title: "Component Path",
      type: "string",
      defaultValue: ({ project, params }) => {
        if (project.hasDirectory("src/components")) {
          return `src/components/${params.name}`;
        }
      },
    },
  },
  isValid: (project) => project.hasDependency("react"),
  run: async ({ project, params, files, self, t }) => {
    for (const file of files) {
      project.writeFile(t(`${params.path}/${file.path}`), t(file.content));
    }
  },
});
Enter fullscreen mode Exit fullscreen mode

Test the script

Now that our script is ready, we should check that it's recognized correctly.

To do that, we can run npx auto ls and see if our script is listed and marked as local:

➜ auto ls
Info: Using main repository: ~/.config/Auto
Info: Using local repository: ~/lab/my-react-project/auto
- <component> Generate a component (local)
Enter fullscreen mode Exit fullscreen mode

To run the script, execute npx auto run <script-id>.
Auto will prompt you for the defined parameters and then run the script.

➜ auto run component
Info: Using main repository: ~/.config/Auto
Info: Using local repository: ~/lab/my-react-project/auto
Info: Running script: component
? Component Name: MyComponent
? Component Path: src/components/MyComponent
Writing file: ~/lab/my-react-project/src/components/MyComponent/index.ts
Writing file: ~/lab/my-react-project/src/components/MyComponent/MyComponent.tsx
Writing file: ~/lab/my-react-project/src/components/MyComponent/MyComponent.test.tsx
Writing file: ~/lab/my-react-project/src/components/MyComponent/MyComponent.stories.tsx
Enter fullscreen mode Exit fullscreen mode

That's it

I hope you like the idea of Auto and that you'll find it helpful!
I'm looking forward to your feedback and contributions!

This post was originally posted on my website, if you’d like to subscribe to my content through RSS you can find the feed there.

Notes:

  • This is a very early version of Auto and many things can be improved.
  • Auto was initially designed for creating global, context-aware generators that could generate different outputs based on project type.
  • The templating & project abstraction part is meant to replace tools like Hygen and Plop.

Top comments (2)

Collapse
 
sebastian_wessel profile image
Sebastian Wessel

😲 your very last sentence made it for me!

I said on Reddit “I probably will try out Auto”, but now it becomes more “I will try to migrate to Auto”

Collapse
 
andrei_fyi profile image
Andrei Neculaesei

Glad you find it interesting! ❤️
Let me know if you hit any bumps or if you have any ideas to extend Auto with cool features / make it easier to use!