Иногда я делаю сразу несколько похожих подпроектов в одном проекте, например, набор статичных лендингов для сбора заявок и блог на AstroJS. Подпроекты отличаются контентом и темой оформления, но используют общие блоки. При этом публиковать общие блоки в публичном пространстве не хочется.
В этом случае полезно иметь монорепу, и я это сделаю без внешних зависимостей только с помощью npm workspaces.
Преимущества monorepo
Эффективность дискового пространства
Устанавливается только одна копия зависимости, общая для нескольких пакетов.
Более быстрая установка
При установке внешних npm-пакетов мы скачиваем только один пакет вместо нескольких копий.
Согласованные версии зависимостей
Все пакеты в npm workspaces используют одну и ту же версию зависимости — больше нет конфликта версий.
Пример проекта
Создадим монорепо для блога:
- внешняя часть сайта: AstroJS
- библиотека компонентов: файлы компонентов
.astro
- библиотека вспомогательных элементов: скрипты, типы, стили
Структура проекта, которую мы хотим получить:
/ ├── node_modules/ ├── packages │ ├── astrojs (делаем сейчас один, потом его дублируем) │ ├── design-components │ └── helpers ├── package.json └── other files
Потом мы можем добавить несколько пакетов с лендингами на AstroJS, используя уже созданные общие блоки.
Для каждого лендинга/блога есть свой AstroJS проект с переменными только для этого проекта:
- контент каждого проекта будет в своей папке c AstroJS:
/astrojs/src/content
; - css-переменные для темы оформления каждого лендинга:
/astrojs/src/styles/theme.css
; - тексты и набор ссылок для меню, шапки и подвала: например,
/astrojs/src/data/linksFooter.json
; - favicons:
/astrojs/public/favicons/favicon.ico
- дефолтные картинки для соцсетей:
/astrojs/public/images/og-default.png
В каждый проект будем подключать:
- локальные файлы с переменными
- общую библиотеку компонентов (получает данные от каждого проекта)
- общие файлы helpers: стили, типы и js/ts-функции (файлы будут получать данные от каждого проекта)
Создание корневого проекта
Создаем корневую папку нашего проекта:
mkdir root-project cd root-project
Инициализируем проект:
npm init -y
Открываем проект в редакторе кода. Я использую VS Code:
code .
Редактируем наш корневой package.json
для всего проекта и указываем, откуда брать дочерние пакеты:
{ "name": "my-blog", "private": true, "workspaces": [ "packages/*" ] }
Создаем папки для наших пакетов:
- root-project / packages / astrojs
- root-project / packages / design-components
Для удобства я сразу создаю файлы .editorconfig
и .nvmrc
в корневой папке.
Создание пакета с AstroJS
Заходим в папку с AstroJS и устанавливаем сам AstroJS:
cd packages/astrojs npm create astro@latest
В процессе установки выбираем:
- установка в текущую папку
- самый простой проект
- зависимости установим потом
- git инициализировать не надо
После установки в папке пакета появляется файл package.json
и необходимые для AstroJS файлы.
Редактируем файл package.json
:
- прописываем название пакета, с учетом корневого
my-blog
- выносим все dependencies пакета AstroJS в dependencies корневого проекта
Код package.json
astrojs-пакета:
{ "name": "@my-blog/astrojs", "type": "module", "version": "0.0.1", "scripts": { "dev": "astro dev", "start": "astro dev", "build": "astro check && astro build", "preview": "astro preview", "astro": "astro", "check": "astro check" } }
Код package.json
корневого пакета:
{ "name": "my-blog", "private": true, "workspaces": [ "packages/*" ], "dependencies": { "astro": "^4.13.1", "typescript": "^5.5.4" }, "devDependencies": { "@astrojs/check": "^0.9.1" } }
Создание пакета с библиотекой компонентов
Работаем с папками и файлами в редакторе кода.
Создаем package.json
в папке packages/design-components
:
{ "name": "@my-blog/design-components", "version": "0.0.1", "type": "module", "private": true }
Создаем папку для компонентов components
и первый компонент Card.astro
(путь от корневого проекта: root-project/packages/design-components/components/Card.astro
)
--- --- <div>Card component</div>
Установка пакетов
Возвращаемся в корневой проект.
Устанавливаем зависимости (все локальные и внешние пакеты):
npm install
Результат: added 412 packages, and audited 415 packages in 1m
В корневом проекте появилась папка node_modules
:
- множество внешних проектов
- папка
@my-blog
с линками на подпапки:@my-blog/astrojs
и@my-blog/design-components
Подключение библиотеки компонентов
Из корневого проекта переходим в наш AstroJS пакет и запускаем его:
cd packages/astrojs npm run dev
В браузере проверяем: http://localhost:4321/ — проект запустился.
Отредактируем tsconfig.json
— добавим import aliases:
{ "extends": "astro/tsconfigs/strict", "compilerOptions": { "baseUrl": ".", "paths": { "@/*": ["./src/*"] } }, "exclude": ["dist"] }
Строка "exclude": ["dist"]
нужна, потому что последнее время AstroJS проверяет папку .dist
при проверке командой astro check
.
Отредактируем нашу главную страницу root-project/packages/astrojs/src/pages/index.astro
и добавим туда наш компонент из локального пакета
--- import Layout from "@/layouts/Layout.astro"; // components import Card from "@my-blog/design-components/components/Card.astro"; --- <Layout title="Welcome to Astro."> <main> <h1>Привет, мир! Это монорепо</h1> <p>Глобальные стили подключены из пакета `helpers`.</p> <Card /> </main> </Layout>
Поздравляю! Теперь вы умеете работать в двумя локальными пакетами.
Подключение темы оформления сайта
Создаем стили: root-project/packages/astrojs/src/styles/theme.css
:root { /* FONTS */ --font-family-base: "Comic Sans MS"; /* COLORS */ --color-theme-pale: #f3e8ff; --color-theme-muted: #d8b4fe; --color-theme-neutral: #a855f7; --color-theme-bold: #7e22ce; --color-theme-intense: #581c87; }
Добавляем файл стилей в root-project/packages/astrojs/src/layouts/Layout.astro
:
--- // styles import "@/styles/theme.css"; interface Props { title: string; } const { title } = Astro.props; --- <!doctype html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="description" content="Astro description" /> <meta name="viewport" content="width=device-width" /> <link rel="icon" type="image/svg+xml" href="/favicon.svg" /> <title>{title}</title> </head> <body> <slot /> </body> </html>
Используем эти переменные стилей всего проекта в компоненте из библиотеки root-project/packages/design-components/components/Card.astro
и добавим получение props:
--- interface Props { title?: string; text?: string; } const { title = "Card title", text = "Card text" } = Astro.props; --- <div class="card"> <p class="title">{title}</p> <p class="text">{text}</p> </div> <style> .card { background-color: var(--color-theme-pale); color: var(--color-theme-intense); padding: 24px 16px; border-radius: 8px; display: flex; flex-direction: column; gap: 8px; } .title { font-weight: 700; } </style>
Заберем в компонент тексты из AstroJS в файле packages/astrojs/src/pages/index.astro
:
--- import Layout from "@/layouts/Layout.astro"; // components import Card from "@my-blog/design-components/components/Card.astro"; --- <Layout title="Welcome to Astro."> <main> <h1>Привет, мир! Это монорепо</h1> <p>Глобальные стили подключены из пакета `helpers`.</p> <Card title="Компонент Card подключен из пакета `design-components`" text="Тексты и переменные для темы оформления подключены из пакета `astrojs`." /> </main> </Layout>
Поздравляю! Теперь ваши компоненты из общей библиотеки могут иметь индивидуальный стиль оформления и тексты, которые задаются в основном проекте.
Создание и подключение пакета helpers
Полезно иметь общие вещи для всех проектов в отдельном пакете.
Создадим пакет в новой папке root-project/packages/helpers
.
Добавим файл package.json
для нового пакета:
{ "name": "@my-blog/helpers", "version": "0.0.1", "type": "module", "private": true }
Создадим общий файл со сбросом стилей root-project/packages/helpers/styles/reset.css
:
*, *::after, *::before { margin: 0; padding: 0; box-sizing: border-box; }
Создадим общий файл глобальных стилей root-project/packages/helpers/styles/global.css
:
body { min-width: 320px; background-color: var(--color-background-page, red); color: var(--color-text-page, blue); line-height: 24px; font-weight: 400; font-size: 16px; font-family: var(--font-family-base, monospace); }
Выходим в корневой проект и устанавливаем наш новый пакет:
# мы были в AstroJS проекте, остановим его: CTRL + C # выходим на уровень корневого проекта: cd ../.. # теперь мы в корневом проекте, устанавливаем новый локальный пакет: npm i
Результат: added 1 package, and audited 419 packages in 1s
Возвращаемся в папку пакета AstroJS и запускаем его снова:
cd packages/astrojs npm run dev
Подключаем файлы стилей из пакета helpers
в Layout root-project/packages/astrojs/src/layouts/Layout.astro
:
--- // styles import "@my-blog/helpers/styles/reset.css"; import "@/styles/theme.css"; import "@my-blog/helpers/styles/global.css"; interface Props { title: string; } const { title } = Astro.props; --- <!doctype html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="description" content="Astro description" /> <meta name="viewport" content="width=device-width" /> <link rel="icon" type="image/svg+xml" href="/favicon.svg" /> <title>{title}</title> </head> <body> <slot /> </body> </html>
Поздравляю! Теперь ваши компоненты отделены от стилей, которые будут одинаковы для всех проектов.
Выводы
После всех этих действий у нас monorepo для множества проектов и минимум зависимостей.
Проверим зависимости на данный момент (только необходимые для самого AstroJS): npm ls
my-blog ├── @astrojs/check@0.9.1 ├── @my-blog/astrojs@0.0.1 -> ./packages/astrojs ├── @my-blog/design-components@0.0.1 -> ./packages/design-components ├── @my-blog/helpers@0.0.1 -> ./packages/helpers ├── astro@4.13.1 └── typescript@5.5.4
Для удобства разработки можно настроить AstroJS проекты по моей инструкции.
В этом случае внешние пакеты устанавливаем в корневой проект:
- stylelint
- prettier
- eslint
- прочие печенюшки.
Если AstroJS проекту понадобятся зависимости типа «@astrojs/react», тоже устанавливаем их в корневой проект.
Часто задаваемые вопросы
ВОПРОС: Нужно ли публиковать наши пакеты в npm?
Публиковать в npm не нужно. Это локальные зависимости, которые живут в вашей же монорепе.
Если вы используете свою библиотеку компонентов на множестве проектов (не только для этого блога), то можете вынести ее как отдельный проект и публиковать ее в npm со своим собственным названием. Тогда в будущих проектах вы будете устанавливать уже внешний пакет из общего npm. Другие пользователи смогут увидеть и использовать вашу библиотеку компонентов в общем npm.
Примера такого решения: библиотека UI компонентов Яндекса. Они могли оставить ее как внутренний проект только в своей монорепе, но вынесли в публичный доступ.
ВОПРОС: Что добавляем в git?
В git идет корневой проект: git init
.
В проекте внутри git у вас будут все ваши локальные пакеты.
Не забудьте проверить, что у вас уходит в git из пакета с AstroJS: в .gitignore
надо запретить
# build output dist/ # generated types .astro/ # dependencies node_modules/
ВОПРОС: какие действия, если устанавливаем уже созданную кем-то монорепу через npm workspaces?
В корневой папке делаем npm install
— установятся все зависимости, как внешние, так и локальные.
ВОПРОС: как работать сразу с несколькими пакетами, например, StrapiJS в качестве CMS и AstroJS как внешнюю часть?
В корневом проекте для package.json
добавляем команды:
{ "name": "my-blog", "workspaces": [ "packages/*" ], { "scripts": { "build": "npm run build:package-a && npm run build:package-b", "build:package-a": "cd packages/package-a && npm run build", "build:package-b": "cd packages/package-b && npm run build" } } }
ссылка на оригинал статьи https://habr.com/ru/articles/833684/
Добавить комментарий