Разработка собственного плагина для nx (executor и generator)

от автора

В продолжении к предыдущей статьи, погружение в nx. Если не знакомы с nx, рекомендую сначала прочитать ее.

Практический урок по написанию собственного плагина. Реализуем generator и executor. generator — при запуске которого у нас будет обновлять корневой файл в проекте projects.json, в которой будем вносить все существующие приложения.

Получившийся результат (проект) можно посмотреть здесь.

А executor будет брать этот файл, читать и выводить в консоль содержимое этого файла.

Пример достаточно простой и не является рекомендацией к его применению на реальных проектах, но дает понять как работают generator и executor.

А в конце статьи расскажу про реальные кейсы.

Поговорим о generator

generator чаще всего вызывается через терминал. Создание собственного generator позволяет упростить повторные операции создания файлов и(-или) папок, как в корне монорепы, так и создавать приложения и библиотеки в ней. Также можно вносить изменения уже в существующие файлы и папки. На сайте nx.dev проекта также можете ознакомится с простым примером создания generator. А мы в свою очередь реализуем собственный (листайте ниже).

Например, у плагина @nx/react есть generator создания библиотеки:

nx g @nx/react:lib my-new-lib

или например для нового микрофронта:

nx g @nx/react:app my-new-app

или создания нового реакт компонента

nx g @nx/react:component my-new-component --project=my-new-app

Можно в принципе зайти в исходники его и взять какие-нибудь решения для своего решения.

Поговорим о executor

Позволяет производить различные манипуляции с приложением, библиотекой. Например: запускать тесты, проверка качества кода, сборка, запуск в режиме разработки и т.д. Под свои нужды можно создать что угодно. Чуть больше информации то, для чего нужен executor и как с ним работать тут. Как и для generator, для executor на сайте проекта также есть пример.

Главное, что executor необходимо указывать в файле project.json приложения или библиотеки. Созданный executor помещается в свойство executor, в options передаются параметры, которые принимает executor. Названия, по которым потом мы будем взаимодействовать с приложением или библиотекой, указываются в targets, например: build, test и т.д.

Итак, приступим к практике.

За основу возьмем шаблон приложения для react.

Подготовка проекта

Начнем с создания приложения:

npx create-nx-workspace@latest myorg
  • Выбираем react

  • На вопрос про framework, отвечаем N.

  • Затем выбираем integrated monorepo.

  • Application name оставляем тот же.

  • Сборщик выбираем webpack.

  • По стилизации оставляем css.

  • На вопрос «Enable distributed caching to make your CI faster» отвечаем N, т.к. не планируем использовать распределенных кэш разработчиков nx.

Микрофронтенды (приложения) будут находится в папке apps, а библиотеки в libs.

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

Если у вас не установлен nx глобально, то можем это сделать так:

npm install -g nx

Запустить приложение в режиме разработке можно так nx serve myorg

Где myorg — название проекта в папке apps, а serve это target, который указан в файле project.json (apps/myorg)

запускаем приложение в режиме разработки

запускаем приложение в режиме разработки

Более детально почитать по тому, что можно делать в текущем окружении можно тут. Там описано как добавлять к монорепе еще микрофронты и библиотеки.

Для начала создадим новый микрофронтенд:

nx g @nx/react:app geek

После выполнения команды, в папке apps появится еще приложение(микрофронтенд) — geek и папка для end-to-end тестов. В итоге, в папке apps у нас два приложения.

А также создадим библиотеку:

nx g @nx/react:lib list

На все вопросы отвечаем нет.

В файле tsconfig.base.json можно заметить, что добавилась строка @myorgg/list": ["libs/list/src/index.ts"], ее добавляет generator плагина react (@nx/react). Добавляется для того, чтобы мы могли в коде использовать импорт библиотеки через публичный интерфейс (public api).

Теперь можно приступить к разработке плагина.

Для того чтобы воспользоваться generator плагина, необходимо установить зависимосить:

npm install -D @nx/plugin@latest

Создадим свой плагин:

nx g @nx/plugin:plugin my-plugin

Разработка собственного generator

nx generate @nx/plugin:generator my-generator --project=my-plugin

После выполнения команды в папке libs/my-plugin/src появится папка generator.

Перейдем в нее и найдем generator/my-generator.

В файле schema.json указываются правила к полям, которые будут проверятся при запуске executor или generator.

Непосредственно nx запускает файл generator.ts.

У generator также может быть папка files, эти файлы будут копироваться в место назначения.

Обновим файл schema.json:

{   "$schema": "http://json-schema.org/schema",   "$id": "MyGenerator",   "title": "",   "type": "object",   "properties": {} }

Файл schema.d.ts можем удалить, т.к. у нас нет входных параметров. Также удалим файл generator.spec.ts, нас не интересуют тесты в данной статье. Папку files тоже удалим, т.к. мы ничего не копируем, за исключением создания файла apps.json в корне проекта.

Откроем файл generator.ts и заменим содержимое на это:

import {   formatFiles,   writeJson,   Tree,   getProjects } from '@nx/devkit';  export async function myGeneratorGenerator(   tree: Tree ) {   const projects = [];   for (const project of getProjects(tree)) {     if (project[1].projectType === 'application') projects.push(project[0]);   }   writeJson(tree, 'apps.json', projects);   await formatFiles(tree); }  export default myGeneratorGenerator;

Проверить работу generator можем так:

nx generate @myorg/my-plugin:my-generator

Теперь в корне репозитория создается файл apps.json с таким содержимым:

[«geek», «geek-e2e», «myorg», «myorg-e2e»]. В условие можно еще указать, чтобы имена проектов -e2e не попадали в файл.

Разработка собственного executor

nx generate @nx/plugin:executor my-executor --project=my-plugin

Обновим файл project.json библиотеки libs/list:

{   "name": "list",   "$schema": "../../node_modules/nx/schemas/project-schema.json",   "sourceRoot": "libs/list/src",   "projectType": "library",   "tags": [],   "targets": {     "lint": {       "executor": "@nx/linter:eslint",       "outputs": ["{options.outputFile}"],       "options": {         "lintFilePatterns": ["libs/list/**/*.{ts,tsx,js,jsx}"]       }     },     "readApps": {       "executor": "@myorg/my-plugin:my-executor"     }   } }

Мы добавили строку generate: {executor: @myorg/my-executor»}, это значит, что мы можем теперь запустить данный executor так:

nx readApps list

Где generate это имя targets, а list — имя проекта. Например мы не сможем такую же команду использовать для приложения geek или myorg, необходимо явно указать в targets файла project.json.

Внес правки в файл executor.ts

import { readJsonFile } from '@nx/devkit'; import { MyExecutorExecutorSchema } from './schema';  export default async function runExecutor(options: MyExecutorExecutorSchema) {   console.log(readJsonFile('apps.json'));   return {     success: true,   }; }

sucess необходимо возвращать, чтобы дать понять nx, что успешно или не успешно выполнилась команда.

После выполнения команды выведется в консоль список приложений из файла apps.json.

Выводы

В примерах выше я постарался как можно проще написать инструкции по взаимодействую с собственным плагином. nx/devkit позволяет использовать граф зависимостей tree в generator, и извлекать полезные данные, не реализую самостоятельно обход по директориям и чтения файлов проектов. А executor в свою очередь кроме options, вторым аргументом принимает executorContext, который содержит множество полезных свойств.

nx это всего лишь инструмент для того, чтобы определенные «сценарии» в проекте упростить, стандартизировать. Но это не значит что вам данный инструмент подойдет. Прежде чем его внедрять, необходимо понимание, что ваших компетенций достаточно чтобы адаптировать этот инструмент для вашего проекта, а также ВРЕМЯ.

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

Если возникнут вопросы или трудности, пишите в комментарии, постараюсь ответить.

Реальные кейсы

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

Можно было ограничиться переездом на yarn workspaces, но тогда нам пришлось писать множество инструментов для работы с пространством, проверки качество кода и т.д.

Поэтому решили пойти по пути меньшего сопротивления и большей эффективности в нашем случае.

Но на yarn мы все равно перешли, но только в роли пакетного менеджера. Но учитываем, что pnp у yarn по умолчанию, необходимо создать файл .yarnrc в проекте и указать nodeLinker: node-modules, чтобы nx взаимодействовал с пакетами корректно.

Переход на yarn увеличил скорость установки пакетов в разы. Раньше разработчик ждал по 10-15 минут при первоначальной установки пакетов. Теперь это может занимать 1-2 минуты.

Вообще чем nx понравился, так это графом зависимостей, что можно упростить себе жизнь при развертывании приложений. Допустим, у нас изменилась библиотека, а эта библиотека используется в нескольких микрофронтах. Благодаря команде nx affected, у нас есть возможность запустить множественную сборку микрофронтов, которые зависят от данной библиотеки.

А теперь о самом плагине в проекте:

  1. реализовали generator для микрофронтедов, библиотек и для создания динамического файла env проекта (берутся переменные окружения и подставляются в результирующий файл, который в свою очередь загружается клиентом и используется микрофронтами).

  2. реализовали executor для webpack. Стандартное @nx/webpack решение не подошло по двум причинам:

    1. часть плагинов и настройки жестко забиты и их нельзя убрать без костылей, обязательно требует чтобы некоторые файлы в проекте присутствовали. У нас например микрофронты лишены своего файла webpack.config.js, executor использует один общий и подставляет определенные настройки под определенные сценарии.

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

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.

Планируете ли использовать пользовательские плагины в nx?

80% Уже использовал4
20% Планирую1
0% Хватает того, что есть0

Проголосовали 5 пользователей. Воздержались 2 пользователя.

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


Комментарии

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

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