Имплементация монорепы
Сейчас будет пример создания трёх приложений и двух библиотек
- Два приложения React: клиентское и административное.
- Библиотека React с именем common-components, которая будет содержать компоненты, совместно используемые проектами React
- А Node.js сервер с именем backend
- Библиотека TypeScript с именами функций, которые будут использоваться серверной частью
Установка
Чтобы создать базовый проект с NX, нам нужно создать workspace, в котором у нас будут находиться проекты
npx create-nx-workspace@latest <project>
У нас в процессе создания монорепы будет возможность выбрать установку для одного проекта и для моддержки монорепозитория. Среда для одного проекта скорее нужна только для оптимизации загрузки пакетов, сборки билдов и CI/CD
Далее нам нужно установить пакеты, которые будут контролировать проекты с определёнными технологиями (установили и добавили в окружение пакеты для работы с нодой, экспрессом, нестом, некстом и реакт). Каждый из этих пакетов предоставляет дополнительные возможности для CLI по установке новых приложений.
# Установит nextjs приложение
nx add @nx/next
# Другие популярные сетапы
nx add @nx/react
nx add @nx/express
nx add @nx/nest
nx add @nx/node
После ввода данной команды мы создаём второй проект. Проекты создаются через вызов установленного проекта @nx/react
с флагом :app
. После этого проект с React создастся в папке apps/admin
, а в apps/backend
проект с нодой
npx nx g @nx/react:app admin
npx nx g @nx/node:app backend
Чтобы создать библиотеку для React-проектов, нам нужно задать через пакет @nx/react
с флагом :lib
папку с распространяемыми компонентами. Флаг :lib
создаёт проект с шейребл компонентами приложения под выбранный стек пакета. Конкретно тут можно расположить общие компоненты для нескольких проектов и они будут собираться из конфига, который находится внутри папки lib/common-components
npx nx g @nx/react:lib common-components
Данная команда создаст общие для всех проектов компоненты
npx nx g @nx/js:lib functions
И на данный момент мы имеем следующую структуру:
|__ apps
| |__ admin
| |__ admin-e2e
| |__ backend
| |__ backend-e2e
| |__ customer
| |__ customer-e2e
|__ libs
| |__ custom-components
| |__ functions
Так же мы можем отобразить граф взаимосвязей в проекте
npx nx graph
Далее мы можем воспользоваться встроенным генератором из nx и сделать react-компонент, который будет доступен сразу в обоих наших react-проектах
npx nx g @nx/react:component header \
--project=common-components --export
libs/common-components/src/lib/header/header.tsx
import styles from './header.module.css';
/* eslint-disable-next-line */
export interface HeaderProps {
text: string;
}
export function Header(props: HeaderProps) {
return (
<header>{props.text}</header>
);
}
export default Header;
Сразу нужно сказать, что все пакеты, которые мы устанавливаем на несколько проектов используются во всех наших приложениях. Тут стоит аккуратнее выбирать зависимости, потому что они будут одинаковы для всех пакетов
npm install axios cors date-fns
Далее нам нужно немного сделать тестовое приложение, которое будет иметь взаимосвязи внутри монорепозитория
apps/admin/src/app/app.tsx
// eslint-disable-next-line @typescript-eslint/no-unused-vars
import styles from './app.module.css';
import { Header } from '@myorg/common-components';
import { useEffect, useState } from 'react';
import axios from 'axios';
export function App() {
const [message, setMessage] = useState('');
useEffect(() => {
axios.get('http://localhost:3000/admin').then((response) => {
setMessage(response.data);
});
}, []);
return (
<div>
<Header text="Welcome to admin!" />
<p>{message}</p>
</div>
);
}
export default App;
apps/customer/src/app/app.tsx
// eslint-disable-next-line @typescript-eslint/no-unused-vars
import styles from './app.module.css';
import { Header } from '@myorg/common-components';
import { useEffect, useState } from 'react';
import axios from 'axios';
export function App() {
const [message, setMessage] = useState('');
useEffect(() => {
axios.get('http://localhost:3000/customer').then((response) => {
setMessage(response.data);
});
}, []);
return (
<div>
<Header text="Welcome to customer!" />
<p>{message}</p>
</div>
);
}
export default App;
libs/functions/src/lib/functions.ts
import { format } from 'date-fns';
export function currentDate(): string {
return format(new Date(), 'yyyy-MM-dd');
}
apps/backend/src/main.ts
import express from 'express';
import { currentDate } from '@myorg/functions';
import cors from 'cors';
const host = process.env.HOST ?? 'localhost';
const port = process.env.PORT ? Number(process.env.PORT) : 3000;
const app = express();
app.use(cors({ origin: '*' }));
app.get('/customer', (req, res) => {
res.send(`[ customer ] ${currentDate()}`);
});
app.get('/admin', (req, res) => {
res.send(`[ admin ] ${currentDate()}`);
});
app.listen(port, host, () => {
console.log(`[ ready ] http://${host}:${port}`);
});
И теперь мы имеем следующие зависимости
Далее в разных терминалах мы можем засёрвить наши приложения
npx nx serve backend
npx nx serve admin
npx nx serve customer
Механизм отслеживания изменений
С помощью гита выделяем изменения в проекте
git add .
git commit -m "Initial commit"
Далее мы можем запустить проверку графом, какие элементы системы были подвержены изменениям
npx nx affected:graph
Данная команда позволяет произвести тестирование на те компоненты, которые были подвержены изменениям
npx nx affected -t test
Самое ключевое слово affected:
выполняет любую операцию чисто только на изменённых проектах, а не на всём окружении