Пишем собственный CLI для React

от автора

Если вы делаете Ctrl+C каждый раз при создании нового компонента в реакте, то эта статья точно для вас!

У реакта нет своего CLI, и понятно почему. Не существует определенных правил, как именно должна выглядеть структура компонента, есть только общие рекомендации в документации. Все разработчики используют структуру, которая прижилась в их команде. А иногда и вовсе приходится поддерживать проекты в разных стилях.

Сама структура также зависит и от используемого стека:

  • Стили — styled, scss modules, css;
  • TypeScript или JavaScript;
  • Тесты

Существует несколько способов облегчить себе жизнь при создании новых компонентов. Например, можно создать шаблоны в вашей среде разработки (например в WebStorm). Но сегодня мы рассмотрим как создавать полную структуру компонента из командной строки. В конце статьи мы сможем создавать компоненты при помощи одной команды. Например, такой как:

npm run create components/Home/ComponentName

Подготовка

Для создания проекта будем использовать Create React App

Создаем проект:

npx create-react-app react-cli

Весь наш код будет хранится в одном файле. Создаем в папку cli в корне нашего проекта, а внутри нее файл create.js.

Для работы нам понадобятся 3 модуля, импортируем их в наш файл.

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

fs — модуль для работы с файловой системой.

path — модуль для обработки путей к файлам.

minimist — модуль для преобразования аргументов из командной строки.

Работа с аргументами

Для того чтобы создать компонент нам нужно передать в командрую строку путь и имя компонента. Мы передадим эту информацию в одной строке (например components/folder1/folder2/Menu), которую потом распарсим на путь и название.

Все аргументы можно достать из объекта process. Допустим, мы ввели в консоль следующую строку:

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

В результате получим:

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

Используя модуль minimist, мы можем преобразовать аргументы в объект:

// 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' // }

Замечательно, с этим уже можно работать.

Создание директорий

Для начала подготовим необходимые переменные. Нам нужен полный путь до папки src нашего проекта, путь из аргументов в виде массива и название компонента.

// cli/create.js  // ...  // достаем путь до папки src текущего проекта const srcPath = [__dirname, '..', 'src'];  // разбиваем путь из аргумента командной строки на массив const arrPath = args.path.split('/');  // достаем последний элемент массива (название компонента) const componentName = arrPath[arrPath.length - 1];

Допустим, мы указали несуществующий путь. По-хорошему, мы должны создать все эти вложенные папки, если их нет. Так и сделаем.

// cli/create.js  // ...  // создание директорий из аргумента (при необходимости) const currentArray = []; arrPath.forEach(element => {   currentArray.push(element);   const currentResolvePath = path.resolve(...srcPath, ...currentArray);   if (!fs.existsSync(currentResolvePath)) { // проверка - существует такая директория или нет?     fs.mkdirSync(currentResolvePath); // если нет, то создаем новую   } });

Здесь мы циклом проходимся по всем элементам пути и при необходимости создаем директорию с помощью метода mkdirSync. До этого нормализуем путь к компоненту в одну строку с помощью метода resolve. После выполнения данных операций у нас будет создана необходимая структура директорий.

Протестируем написанное. Вводим в командную строку следующую команду (при этом у нас пока нет никаких директорий в папке src):

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

И мы получим следующий результат:

Создание файлов компонента

Отлично, пол дела сделано, осталось создать файлы компонента.

Мы будем использовать самую простую структуру компонента:

  • Для стилей обычный css
  • Без TS
  • Без тестов
  • Функциональный компонент

Получается, нам нужно создать 3 файла.

1. Шаблон компонента

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

2. Шаблон индексного файла

export { default } from './CustomComponent';

3. Шаблон файла стилей

.wrapper {}

Для начала достанем в одну переменную полный путь до компонента (включая личную папку компонента):

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

Новые файлы создаются при помощи команды writeFileSync, которая принимает путь до файла и содержимое.

Создание файла компонента:

// 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);

Создание индексного файла:

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

Создание файла стилей:

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

Готово!

Теперь посмотрим что у нас получилось.

// 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);

Получилось всего 43 строки с учетом комментариев, неплохо для такой полезной штуки!

Теперь попробуем создать компонент:

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

Все получилось! Остался последний штрих…

Добавление команды в package.json

Добавим команду в файл package.json, чтобы каждый раз не писать путь к скрипту

{   "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"     ]   } }

Теперь вместо:

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

можем просто написать

npm run create components/folder1/folder2/Button

Исходный код проекта можно посмотреть на гитхабе


ссылка на оригинал статьи https://habr.com/ru/post/477780/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *