Проблема

Я очень много работаю в проектах на React, я постоянно сталкивался с одной и той же проблемой: создание повторяющихся структур компонентов.

Каждый новый компонент — это:

  • Нужно создать директорию с компонентом

  • ComponentName.tsx — основной файл компонента

  • ComponentName.module.css — стили

  • index.ts — файл реэкспорта

  • ComponentName.stories.tsx — сторибук

  • ComponentName.test.tsx — тесты

  • и т.д. у каждого свое в зависимости от проекта

В каждом из этих файлах своя структура возможно есть какие то кроссимпорты между этими файлами.

Причем эта проблема не только во фронт проектах, каждый хороший проект поддерживает какую то структуру.

Конечно можно было бы использовать сниппеты, но каждый файл все равно нужно было создать дать ему название и это постоянно повторяется. Мне же нужно было создавать целые группы связанных файлов одной командой.

Первый мой подход в этом направлении был через написание экстеншена для VSCode с ним можно ознакомиться тут или найти по имени re-create в магазине расширений.

Вдохновение от NestJS

Меня впечатлило, как в NestJS CLI работает генерация кода:

nest generate module users
nest generate controller users
nest generate service users

Одна команда — и создается полная структура с правильными импортами и шаблонным кодом. Мне захотелось подобного удобства для других проектов.

Поиск решения

Что я искал:

  • Декларативное описание шаблонов в YAML/JSON

  • Поддержка динамических параметров (как Jinja2)

  • Установка через npm — привычный workflow

  • Локальные и глобальные шаблоны

  • Простота использования и расширения

  • Возможность дополнения уже созданных файлов

Что я нашел (и почему не подошло):

1. Yeoman — мощный, но перегруженный

  • Слишком сложная настройка генераторов

  • Требует написания JavaScript-кода для каждого шаблона

  • Нет декларативного подхода

2. Plop.js — проще, но все равно JS-код

  • Лучше Yeoman, но все равно требует программирования

  • Нет поддержки Jinja2-синтаксиса из коробки

3. Hygen — неплохо, но ограниченно

  • Текстовые шаблоны с ограниченной логикой

  • Нет поддержки сложных структур

4. Cookieсutter — Python-ориентированный

  • Требует Python в системе

  • Не интегрируется с npm-экосистемой

Свое решение на Go

Почему Go?

  • Производительность: Мгновенное выполнение команд

  • Статическая компиляция: Один бинарный файл без зависимостей

  • Простота: Идеален для CLI-утилит

  • Интерес: Хотелось попрактиковать новый язык

Удалось реализовать MVP такой утилиты и внедрить в свой проект на работе. С кодом реализации можно ознакомиться в репозитории и можно посмотреть npm пакет

Что получилось в реализации:

  1. YAML-конфигурация — декларативное описание шаблонов

  2. Jinja2-синтаксис — знакомый шаблонизатор для фронтендеров

  3. Глобальные + локальные шаблоны — гибкость использования

  4. Установка через npm — можно хранить как утилиту в devDependency фронтового проекта

  5. Мультиплатформенность — при помощи Go можно сделать бинарный файл под любую платформу

Пример использования:

# Установка
npm install -D lekalo

Конфигурация может лежать локально в проекте или находиться глобально в системе

templates:
  react-component:
    params:
      - name: "name"
        prompt: "Enter component name"
      - name: "path"
        prompt: "Enter path"
        default: "./"
    files:
      component:
        path: "{{ path }}/{{ name }}.tsx"
        template: |
          import React from 'react';
          interface {{ name }}Props {}
          export const {{ name }}: React.FC<{{ name }}Props> = () => {
            return <div>{{ name }}</div>;
          }
      index:
        path: "{{ path }}/index.ts"
        template: |
          export { {{ name }} } from './{{ name }}'
# Создание компонента
lekalo gen react-component componentName=Button path=./src/components

Поскольку мы на проекте используем FSD мы сделали несколько npm скриптов в package.json где указали статически параметры для пути где будет создаваться компонент, а его название ты обьявляешь в терминальном диалоге:

❯ npm run create-widget

> lekalo gen widget path=./src/3_widgets

Enter widget name: AdditionalServices
Создан файл: ./src/3_widgets/AdditionalServices/index.ts
Создан файл: ./src/3_widgets/AdditionalServices/AdditionalServices.translate.ts
Создан файл: ./src/3_widgets/AdditionalServices/AdditionalServices.ru.json
Создан файл: ./src/3_widgets/AdditionalServices/AdditionalServices.tsx
Создан файл: ./src/3_widgets/AdditionalServices/AdditionalServices.module.css
Создан файл: ./src/3_widgets/AdditionalServices/AdditionalServices.state.ts
Создан файл: ./src/3_widgets/AdditionalServices/AdditionalServices.adapter.ts

Результаты

Теперь кастомно собранный SPA на React проект работает как фреймворк и ускоряет работу при расширении функционала

Что изменилось после создания внедрения Lekalo на проект:

  • ✅ Скорость: Создание компонента теперь занимает 5 секунд вместо 2 минут

  • ✅ Консистентность: Все компоненты создаются по единому стандарту

  • ✅ Масштабируемость: Легко добавлять новые шаблоны для разных задач

  • ✅ Доступность: Коллеги могут использовать одинаковые шаблоны

Неожиданные преимущества:

  • Шаблоны стали документацией стандартов кода

  • Возможность быстрого прототипирования новых структур

  • Снижение порога входа для новичков в проекте

P.S.

Если кто-то решит попробовать можете со мной связаться я смогу помочь с внедрением на ваш проект, также буду раз фидбеку или предложениям. Можно использовать issues на гитхаб или написать мне в телеграмм @i_mkhl

Спасибо.

Комментарии (7)


  1. AsalTash
    02.09.2025 18:53

    Очень любопытно, надо проверить как будет на деле работать, спасибо!


  1. paramtamtam
    02.09.2025 18:53

    Занятно! Вот только ничего что ваш npm пакет весит ~200Mb (из-за того что все возможные комбинации бинарей в нем), да и как-то органичнее было бы для npm сделать свой велосипед проект на node, чтоб "одна среда, один язык" да без внешних зависимостей.. Как мне кажется, как иследдовательская работа - прикольно! Как тула чтоб кому-то посоветовать - врятли.


    1. iMkhl Автор
      02.09.2025 18:53

      Хорошее замечание! Да в репозитории есть ишью на это. Проблема не в том что нужно писать именно на ноде чтобы пакет немного весил. Можно поставлять в npm пакете в зависимостях компилятор Go и исходные файлы на go. Билд будет происходить при установке из npm под конкретную систему.

      Вначале сделал как было проще, для проверки концепции. Обязательно доработаю.


      1. paramtamtam
        02.09.2025 18:53

        Билд будет происходить при установке из npm под конкретную систему

        Пожалуйста, не надо так делать :( Хотя, npm-экосистему уже врятли что-то спасет..


        1. iMkhl Автор
          02.09.2025 18:53

          Почему такое мнение? Очень много инструментов использует подобный подход компилируя разные языки для утилит на ноде.

          Это сделано для того чтобы можно было поставлять в другие экосистемы один и тот же код а не писать каждую реализацию на каждом языке под свою экосистему.


          1. paramtamtam
            02.09.2025 18:53

            Как-то мне приходилось собирать проект на ноде, который в зависимоятях тянул пакет что использует python, который через свое подобие FFI запускал функции из rust. А потом его надо было собрать под arm или какая-то из зависимостей обновилась, но не ее зависимость.. Ад был тот еще. Да и не только с нодовыми проектами в такое влетал.

            Позиция заключается в том, что "если ты можешь - это не значит что ты должен" + Hell is paved with good intentions. Если ты хочешь делать что-то подобное то, скорее всего, твоя дорога сворачивает не туда. Да, ваш проект крохотный, но импакт и пример другим заставит их думать "ну норм же, вон, другие так делают".

            Очень много инструментов использует подобный подход

            Скорее всего тех, где просто нет возможности иначе. Вот нет её. Вот совсем. Ну невозможно иначе. Никак, ваапще. Если есть хоть малейшая возможность не раздувать зависимости - не надо делать этого. Пардон, но вы имеете желание ради парсинга строк и сисколов создания директорий/записи файлов, в которые нода умеет прекрасно - тянуть целиком компилятор другого языка с исходниками собственной тулы, да компилить на целевой тачке во время установки зависимостей. Неужели этого не смущает по умолчанию? ;)


            1. iMkhl Автор
              02.09.2025 18:53

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

              Я думаю что подобных утилит для дев окружения которые просто повышают DX это не критично один раз поставилось скомпилировалось автоматически и забыли, продакшен сборка и CI даже ничего не знает про это. Даже если она не запустится на какой нибудь специфичной платформе это не критическая проблема. Но вероятно что где то не запустится, поэтому поставлять бинарь поидее самое надежное, но и в реализации на ноде данного проекта могли бы возникнуть проблемы с зависимостями парсера Yaml и шаблонизатора Jinja. Вопрос требует детального изучения

              А что вы думаете про WASM? Тоже плохая идея?