Writing your own CLI for React

If you do Ctrl+C



every time you create a new component in the reaction, then this article is for you!













The reaction does not have its own CLI, and it is understandable why. There are no specific rules for how the structure of the component should look like, there are only general recommendations in the documentation. All developers use a structure that has taken root in their team. And sometimes you have to support projects in different styles.







The structure itself also depends on the stack used:









There are several ways to make your life easier when creating new components. For example, you can create templates in your development environment (for example, in WebStorm). But today we will look at how to create a complete component structure from the command line. At the end of the article, we can create components with a single command. For example, such as:







 npm run create components/Home/ComponentName
      
      





Training



We will use the Create React App to create the project .







Create a project:







 npx create-react-app react-cli
      
      





All our code will be stored in one file. We create the cli folder in the root of our project, and inside it create.js file.







To work, we need 3 modules, we import them into our file.







 // cli/create.js const fs = require('fs'); const path = require('path'); const minimist = require('minimist');
      
      





fs - module for working with the file system.







path - a module for processing file paths.







minimist - a module for converting arguments from the command line.







Work with Arguments



In order to create a component, we need to pass the path and name of the component to the command line. We will pass this information on one line ( for example, components/folder1/folder2/Menu



), which we then parse into the path and name.







All arguments can be retrieved from the process



object. Suppose we enter the following line into the console:







 node cli/create.js --path components/folder/Menu
      
      





As a result, we get:







 console.log(process.argv); // [ // '/usr/local/bin/node', // '/Users/a17105765/projects/react-cli/cli/create.js', // '--path', // 'components/folder/Menu' // ]
      
      





Using the minimist module, we can convert the arguments to an object:







 // cli/create.js // ... const args = minimist(process.argv); console.log(args); // { // _: [ // '/usr/local/bin/node', // '/Users/a17105765/projects/react-cli/cli/create.js' // ], // path: 'components/folder/Menu' // }
      
      





Great, you can already work with this.







Creating Directories



First, prepare the necessary variables. We need the full path to the src folder of our project, the path from the arguments as an array and the name of the component.







 // cli/create.js // ... //     src   const srcPath = [__dirname, '..', 'src']; //         const arrPath = args.path.split('/'); //     ( ) const componentName = arrPath[arrPath.length - 1];
      
      





Suppose we have indicated a nonexistent path. In a good way, we must create all these subfolders if they are not there. So let's do it.







 // cli/create.js // ... //     ( ) const currentArray = []; arrPath.forEach(element => { currentArray.push(element); const currentResolvePath = path.resolve(...srcPath, ...currentArray); if (!fs.existsSync(currentResolvePath)) { //  -     ? fs.mkdirSync(currentResolvePath); //  ,    } });
      
      





Here we loop through all the elements of the path and, if necessary, create a directory using the mkdirSync



method. Before that, we normalize the path to the component in one line using the resolve



method. After performing these operations, we will create the necessary directory structure.







Test written. We enter the following command into the command line (at the same time, we don’t have any directories in the src



folder yet):







 node cli/create.js --path components/A/B/C/D/E/CustomComponent
      
      





And we get the following result:













Creating component files



Well done, done, all that remains is to create the component files.







We will use the simplest component structure:









It turns out that we need to create 3 files.







1. Component template



 import React from 'react'; import './CustomComponent.css'; const CustomComponent = () => { return ( <div className="wrapper"> </div> ); }; export default CustomComponent;
      
      





2. Index file template



 export { default } from './CustomComponent';
      
      





3. Style file template



 .wrapper {}
      
      





First, get the full path to the component in one variable (including the component’s personal folder):







 // cli/create.js // ... const componentPath = [...srcPath, ...arrPath];
      
      





New files are created using the writeFileSync



, which takes the file path and contents.







Creating a component file:







 // cli/create.js // ... const componentCode = `import React from 'react'; import './${componentName}.css'; const ${componentName} = () => { return ( <div className="wrapper"> </div> ); }; export default ${componentName};`; fs.writeFileSync(path.resolve(...componentPath, `${componentName}.jsx`), componentCode);
      
      





Creating an index file:







 // cli/create.js // ... const indexCode = `export { default } from './${componentName}';`; fs.writeFileSync(path.resolve(...componentPath, 'index.js'), indexCode);
      
      





Creating a stylesheet:







 // cli/create.js // ... const styleCode = '.wrapper {}'; fs.writeFileSync(path.resolve(...componentPath, `${componentName}.css`), styleCode);
      
      





Done!







Now let's see what happened.







 // cli/create.js const fs = require('fs'); //       const path = require('path'); //     const minimist = require('minimist'); //        const args = minimist(process.argv); const srcPath = [__dirname, '..', 'src']; //    src   const arrPath = args.path.split('/'); //         const componentName = arrPath[arrPath.length - 1]; //   -   //     ( ) const currentArray = []; arrPath.forEach(element => { currentArray.push(element); const currentResolvePath = path.resolve(...srcPath, ...currentArray); if (!fs.existsSync(currentResolvePath)) { //  -     ? fs.mkdirSync(currentResolvePath); //  ,    } }); const componentPath = [...srcPath, ...arrPath]; //   const componentCode = `import React from 'react'; import './${componentName}.css'; const ${componentName} = () => { return ( <div className="wrapper"> </div> ); }; export default ${componentName};`; fs.writeFileSync(path.resolve(...componentPath, `${componentName}.jsx`), componentCode); //    const indexCode = `export { default } from './${componentName}';`; fs.writeFileSync(path.resolve(...componentPath, 'index.js'), indexCode); //    const styleCode = '.wrapper {}'; fs.writeFileSync(path.resolve(...componentPath, `${componentName}.css`), styleCode);
      
      





It turned out only 43 lines, taking into account the comments, not bad for such a useful thing!







Now let's try to create a component:







 node cli/create.js --path components/folder1/folder2/Button
      
      











Everything worked out! There is the last touch ...







Adding a command to package.json



Add the command to the package.json file so that each time we don’t write the path to the script







 { "name": "react-cli", "version": "0.1.0", "private": true, "dependencies": { "react": "^16.12.0", "react-dom": "^16.12.0", "react-scripts": "3.2.0" }, "scripts": { "start": "react-scripts start", "build": "react-scripts build", "test": "react-scripts test", "eject": "react-scripts eject", "create": "node cli/create.js --path" }, "eslintConfig": { "extends": "react-app" }, "browserslist": { "production": [ ">0.2%", "not dead", "not op_mini all" ], "development": [ "last 1 chrome version", "last 1 firefox version", "last 1 safari version" ] } }
      
      





Now instead of:







 node cli/create.js --path components/folder1/folder2/Button
      
      





we can just write







 npm run create components/folder1/folder2/Button
      
      





The source code of the project can be viewed on the github








All Articles