img

Adding new components to business development can become a repetitive and tedious task over time, especially if you’re writing tests and using something like Storybook, the file structure of the individual components might look like this

img

You can save a lot of time by writing a Node.js script to get the required component templates and generate these files instead of writing them manually each time.

There are several NPM packages available for you to download and try out, but the added benefit of this approach is that you can adapt the component template to suit your and your team’s needs on a project basis.

What would you need?

  1. File Template Function
  2. Node scripts for creating and populating files
  3. Logic in the script to update the components/index.ts file (optional)
  4. NPM scripts to run node scripts (optional)

Let’s start by creating a directory in the root folder of the project to hold the template functions and logic.

take .generate_component
touch component_templates.js
touch index.js
Copy the code

Component templates

An example of the structure of the basic components used by my team is as follows:

import React from 'react';

import './Navbar.scss';

export interface INavbarProps {}
 const Navbar = ({}: INavbarProps) => {  return <div>Hello 👋, I am a Navbar component.</div>; };  export default Navbar; Copy the code

components/Navbar/Navbar.tsx

In our component_templates.js file, we can add a function like this to generate component templates.

exports.component = name => `
  import React from 'react';
  import './${name}.scss';
  export interface I${name}Props {}
  const ${name}= ({} :${name}Props) => {
 return <div>Hello 👋, I am a ${name} component.</div>;  };  export default ${name}; `; Copy the code

.generate_component/component_templates.js

This function will receive the name parameter when the Node script is run.

Depending on the file you want to include the folder structure, you can add additional component templates. In my example, I want to add the following template functions:

  • component
  • story
  • test

The complete file for the folder structure looks like this:

// component.tsx
exports.component = name => `
  import React from 'react';
  import './${name}.scss';
  export interface I${name}Props {}
 const ${name}= ({} :${name}Props) => {  return <div>Hello 👋, I am a ${name} component.</div>;  };  export default ${name}; `;  // component.stories.jsx exports.story = name => `  import React from 'react';  import ${name} from './${name}.tsx';  export default {  title: '${name}'. component: ${name}. };  export const Default = () => <${name}/ >;`;  // component.test.tsx exports.test = name => `  import React from 'react';  import { render } from '@testing-library/react';  import ${name} from './${name}';  describe('${name} Component', () = > { test('it should match the snapshot', () = > { const { asFragment } = render(<${name}/ >); expect(asFragment()).toMatchSnapshot();  });  }); `;  // index.ts exports.barrel = name => `  import ${name} from './${name}';  export default ${name}; `; Copy the code

.generate_component/component_templates.js

The Node script

The node script does the following:

  1. When running the script, accept a parameter from the terminal (for the component name)
  2. Create a folder for the new component
  3. Add the required files to the folder
  4. Read from the components/index.ts file and insert the new component into it (optional)

This assumes that you export all the components in the Components folder using an index.js. Here is an example of index.js we used for reference

import Carousel from './Carousel';
import GenreCard from './GenreCard';
import HeroBanner from './HeroBanner';
import Layout from './Layout';
import Navbar from './Navbar';
import NetworkCard from './NetworkCard'; import Poster from './Poster'; import Swimlane from './Swimlane';  export {  Carousel,  GenreCard,  HeroBanner,  Layout,  Navbar,  NetworkCard,  Poster,  Swimlane, }; Copy the code

components/index.js

If you don’t follow the pattern of including the newly generated component in components/index.ts, I recommend removing the code block in the example below, after the comment marked “Optional”.

const fs = require('fs');
const { component, story, test, barrel } = require('./component_templates.js');

// grab component name from terminal argument
const [name] = process.argv.slice(2);
if(! name) throw new Error('You must include a component name.');  const dir = `./src/components/${name}/ `; // throw an error if the file already exists if (fs.existsSync(dir)) throw new Error('A component with that name already exists.');  // create the folder fs.mkdirSync(dir, { recursive: true });  function writeFileErrorHandler(err) {  if (err) throw err; }  // component.tsx fs.writeFile(`${dir}/${name}.tsx`, component(name), writeFileErrorHandler); // component.scss fs.writeFile(`${dir}/${name}.scss`, ' ', writeFileErrorHandler); // storybook.jsx fs.writeFile(`${dir}/${name}.stories.jsx`, story(name), writeFileErrorHandler); // test.tsx fs.writeFile(`${dir}/${name}.test.tsx`, test(name), writeFileErrorHandler); // index.tsx fs.writeFile(`${dir}/index.ts`, barrel(name), writeFileErrorHandler);  //////////////// /// Optional /// ////////////////  // insert new component into 'components/index.ts file fs.readFile('./src/components/index.ts', 'utf8', function(err, data) {  if (err) throw err;   // grab all components and combine them with new component const currentComponents = data.match(/(? <=import )(.*?) (? = from)/g); const newComponents = [name, ...currentComponents].sort();   // create the import and export statements  const importStatements = newComponents  .map(importName => `import ${importName} from '. /${importName}'; \n`) .join('');  const exportStatements = `export {\n${newComponents  .map(component => ` ${component},\n`)  .join('')}}; \n`;  const fileContent = `${importStatements}\n${exportStatements}`;   fs.writeFile(`./src/components/index.ts`, fileContent, callback); }); Copy the code

.generate_component/index.js

Now that the script and template functions are in place, you can run the script by typing the following command in the terminal

node ./generate_component ComponentName
Copy the code

Alternatively, if you don’t want to run node scripts directly, you can write an NPM script to run the command package.json file in a package. Such as:

"scripts": {
  "generate-component": "node .generate_component The $1"
}
Copy the code

The $1 in the above script will be the name of the argument passed to the NPM script when it runs.

Now that you have written the NPM script, you can run the following commands from your terminal

npm run generate-component ComponentName
Copy the code

If everything is done correctly, the new folder should appear in the Components folder and be added to the Components/index.ts file.

Hopefully you can incorporate this pattern into your workflow and save a lot of time!

👉 How to generate React component code from the console