lerna js что это

Lerna js что это

lerna js что это

A tool for managing JavaScript projects with multiple packages.

About

Splitting up large codebases into separate independently versioned packages is extremely useful for code sharing. However, making changes across many repositories is messy and difficult to track, and testing across repositories gets complicated really fast.

To solve these (and many other) problems, some projects will organize their codebases into multi-package repositories. Projects like Babel, React, Angular, Ember, Meteor, Jest, and many others develop all of their packages within a single repository.

Lerna is a tool that optimizes the workflow around managing multi-package repositories with git and npm.

Getting Started

Let’s start by installing Lerna globally with npm:

Lerna 2.x is the recommended version to start with.

Next we’ll create a new git repository:

And now let’s turn it into a Lerna repo:

Your repository should now look like this:

Commands

Here is a brief introduction for each command. Please visit the README for more detailed informations.

lerna init

Create a new lerna repo or upgrade an existing repo to the current version of Lerna.

Options

lerna bootstrap

Bootstrap the packages in the current Lerna repo. Installing all their dependencies and linking any cross-dependencies.

This command is crucial, as it allows you to use your package names in require() as if the packages were already existing and available in your node_modules folder.

lerna import

Import the package in the local path

into packages/ with commit history.

lerna publish

Create a new release of the packages that have been updated. Prompts for a new version and updates all the packages on git and npm.

Options

—npm-tag [tagname] — Publish to npm with the given npm dist-tag (Defaults to latest).

—skip-git – Don’t run any git commands.

—force-publish [packages] — Force publish for the specified packages (comma-separated) or all packages using * (skips the git diff check for changed packages).

lerna changed

Check which packages have changed since the last release.

lerna diff [package?]

Diff all packages or a single package since the last release.

lerna run [script]

Run an npm script in each package that contains that script.

lerna ls

List all of the public packages in the current Lerna repo.

Источник

Монорепозитарии на примерах с использованием Lerna. Часть 1

Определения монорепозитария могут быть разными, но мы будем считать репозитарий как моно если выполняются следующие условия :

Так же еще нужно учесть что рост каждого репозитория по отдельности обусловлен следующими параметрами:

Вместо то что бы высказываться за или против использования монорепозитарий я планирую в этой статье исследовать несколько практических вопросов использования их. В частности для управления проектов, я планирую использовать инструмент Lerna на примере тестового проекта monorepo (проект будет на JavaScript).

Финальная версия проекта monorepo доступна по ссылке.

Введение

Прежде чем мы перейдем к созданию проекта, на нужно установить Lerna. Хорошая новость в том, что это очень просто.

Установите Lerna глобально командой:

примечание: это статься написана с использованием Node.js v8.9.4 и Lerna v2.9.0.

Создайте новую папку и в ней запустите следующие команды для инициализации Lerna.

В результате должна появиться следующая структура файлов:

lerna js что это

Создание Packages

Теперь, когда у нас есть проект monorepo c Lerna, мы можем начать создавать пакеты. В контексте развертывания в npm Registry пакетами будут отдельные пакеты npm. То есть каждый пакет npm по сути будет отдельный проект. Так же пакеты будут иметь слабую связь между собой и независимое управление зависимостями (подробнее об этом позже).

Мы создаем отдельные проекты, создавая папки в директории packages, например, apple, banana и grocery, и запустим следующую команду в каждой из них:

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

lerna js что это

Сторонние зависимости

Допустим что нам нужно что бы все три проекта зависили бы от npm пакета sillyname@0.0.3 (с определенной версией); для этого нам нужно запустить команду (команды Lerna могут быть запущены в любой папке проекта):

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

lerna js что это

В итоге в каждом пакете своя копия пакета sillyname; что не очень хорошо с точки зрения потребления дискового пространства и более медленной процедуры инсталляции. Для решения этих проблем можно вручную обновить каждый package.json и установить пакет sillyname только один раз в родительскую папку node_modules ( Node.js будет искать пакеты в родительских папках). Но это достаточно утомительно.

У Lerna есть другое решение; опция hoist:

В результате структура папок будет такой (на скриншете не показа родительская папка node_modules):

lerna js что это

В итоге мы получим:

Выборочное обновление

Допустим нам нужно обновить версию sillyname только для проекта grocery; для этого нам нужно запустить команду:

lerna add sillyname@0.1.0 —scope=grocery

В результате получим такую структуру папок (на скриншете не отображена корневая папка node_modules):

lerna js что это

Внутренние зависимости

Сейчас допустим что нам надо что бы пакет grocery зависил от пакетов apple и banana. Для запустим команду:

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

lerna js что это

Код

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

Далее выполним следующую команды из папки пакета grocery:

Мы должны получить что-то вроде этого:

Закончив с кодирование, внесем изменения в .gitignore, так как нам не нужно хранить папку node_modules в системе контроля версий.

Затем добавьте файлы в коммит и отправим проект в удаленный репозиторий.

Запуск (Bootstrap)

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

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

Все этого достаточно что бы приступить к работе над кодом.

Источник

Русские Блоги

Введение в lerna

lerna###

Как выглядит проект lerna? ###

Что умеет Лерна? ###

Две команды lerna bootstrap и lerna publish в основном используются в Lerna.

Начните наш путь открытий ###

установка######
Создайте новый репозиторий git и инициализируйте его с помощью lerna ######

Теперь наш проект должен выглядеть так:

How it works####

Lerna предоставляет два типа режимов управления проектами: фиксированный и независимый.

Commands####

Создайте новый или обновите существующий проект lerna

bootstrap####

publish####

Опубликовать текущий проект
Он создает новый выпуск, генерирует новую версию, выполняет git commit / tag и публикует в npm

Отметив «private»: true в package.json, пакет не будет выпущен.

—npm-tag [tagname]####

Обычно последний тег устанавливается с помощью команды npm install my-package, а предварительную версию можно установить с помощью npm install my-package @ prerelease

When run with this flag, publish publishes packages in a more granular way (per commit). Before publishing to npm, it creates the new version tag by taking the current version and appending the current git sha (ex: 1.0.0-alpha.81e3b443
).

The intended use case for this flag is a per commit level release or nightly release.

—skip-git####

When run with this flag, publish will publish to npm without running any of the git commands.

Only publish to npm; skip committing, tagging, and pushing git changes (this only affects publish).

—skip-npm####

Only update versions and dependencies; don’t actually publish (this only affects publish).

—force-publish [packages]####

When run with this flag, publish will force publish the specified packages (comma-separated) or all packages using *
.

This will skip the lerna updated check for changed packages and forces a package that didn’t have a git diff change to be updated.

When run with this flag, publish will skip all confirmation prompts. Useful in Continuous integration (CI) to automatically answer the publish confirmation prompt.

—repo-version####

When run with this flag, publish will skip the version selection prompt and use the specified version. Useful for bypassing the user input prompt if you already know which version to publish.

updated####

Check which packages have changed since the last release (the last git tag).

clean####

Diff all packages or a single package since the last release.

Similar to lerna updated. This command runs git diff

Run an npm script in each package that contains that script.

Hint: The commands are spawned in parallel, using the concurrency given. The output is piped through, so not deterministic. If you want to run the command in one package after another, use it like this:

Источник

Создаем монорепозиторий с помощью lerna & yarn workspaces

lerna js что это

За последние несколько лет концепция монорепозиториев успешно зарекомендовала себя, так как позволяет значительно упростить процесс разработки модульных программных проектов, таких как инфраструктуры на основе микросервисов. Основные преимущества такого архитектурного подхода очевидны на практике, поэтому предлагаю создать свой тестовый монорепозиторий с нуля, попутно разбираясь в нюансах работы с yarn workspaces и lerna. Ну что ж, начнём!

Рассмотрим структуру нашего проекта, который будет представлять собой три библиотеки расположенные в папке packages/, а также package.json в корневой директории.

Подразумевается, что у нас есть две независимые библиотеки first и second, а также библиотека app, которая будет импортировать функции из первых двух. Для удобства все три пакета помещены в директорию packages. Можно было оставить их в корневой папке или поместить в директорию с любым другим именем, но, для того чтобы следовать общепринятым конвенциям, мы разместим их именно таким образом.

Библиотеки first и second для простоты эксперимента будут содержать всего по одной функции в index.js, каждая из которых будет возвращать строку приветствия от имени модуля. На примере first выглядеть это будет следующим образом:

В модуле app мы будем выводить в консоль сообщение Hi from the app, а также приветствия из двух других пакетов:

Чтобы first и second были доступны в app, обозначим их как зависимости в dependencies.

Кроме того, для каждой библиотеки добавим в локальный package.json префикс @monorepo/ в значении name перед основным именем пакета.

Аналогией из реальной жизни для нашего тестового проекта могут быть различные библиотеки для работы с данными, инструменты валидации, аналитики или просто набор UI-компонентов. Если же предположить, что мы собираемся разрабатывать web и mobile приложение (например, используя React и React Native соответственно), и у нас есть часть переиспользуемой логики, возможно, стоит вынести её в отдельные компоненты, чтобы потом использовать в других проектах. Добавим к этому сервер на Node.js и получится вполне реальный кейс из жизни.

Yarn workspaces

Последним штрихом перед созданием полноценного монорепозитория будет оформление package.json в корне нашего репозитория. Обратите внимание на свойство workspaces — мы указали значение packages/*, что означает «все подразделы в папке packages». В нашем случае это app, first, second.

Кроме того, в package.json нужно обязательно указать «private»: true, так как workspaces доступны только в приватных проектах.

Для того чтобы всё взлетело, выполним команду yarn (аналог yarn install или npm install) из корневой директории. Поскольку зависимости, которые есть в модуле app, определены как workspaces в корневом package.json, фактически, мы ничего не скачаем из npm-registry, а просто свяжем («залинкуем») наши пакеты.

lerna js что это

lerna js что это

Давайте разберемся, как это работает. Вызвав yarn, мы создали в node_modules символические ссылки на наши директории в папке packages.

lerna js что это

Благодаря такой связи в зависимостях, мы получили одно большое преимущество — теперь при изменении в модулях first и second наше приложение app получит актуальную версию этих пакетов без пересборки. На практике это очень удобно, т.к. мы можем вести локальную разработку пакетов, по-прежнему определяя их как сторонние зависимости (какими они и становятся в конечном итоге).

Следующим важным преимуществом, которое можно получить от работы с yarn workspaces, является организация хранения сторонних зависимостей.

Предположим, что мы захотели использовать библиотеку lodash в first и second. Выполнив команду yarn add lodash из соответствующих директорий мы получим обновление локальных package.json — в dependencies появится актуальная версия пакета.

Что же касается самого пакета lodash — физически библиотека будет установлена в node_modules на корневом уровне один раз.
Если же необходимая версия внешнего пакета (в нашем случае lodash) отличается для first и second (например в first нужна lodash v3.0.0, а в second v4.0.0), то в корневой node_modules попадет пакет с более низкой версией (3.0.0), а версия lodash для модуля second будет храниться в локальном packages/second/node_modules.
Кроме плюсов у такого подхода могут быть незначительные минусы, которые yarn позволяет обойти с помощью дополнительных флагов. Подробнее о таких нюансах можно прочитать в официальной документации.

Добавляем Lerna

Из корневой директории проинициализируем lerna:

lerna js что это

Фактически, мы выполнили сразу несколько действий с помощью одной команды: создали git репозиторий (если он не был проинициализирован до этого), создали файл lerna.json и обновили наш корневой package.json.

Теперь в только что созданном файле lerna.json добавим две строчки — «npmClient»: «yarn» и «useWorkspaces»: true. Последняя строка говорит о том, что мы уже используем yarn workspaces и нет необходимости создавать папку app/node_modules с символические ссылками на first и second.

Тесты с Lerna

Для того чтобы показать удобства работы с lerna добавим тесты для наших библиотек.
Из корневой директории выполним установку пакета для тестирования — jest. Выполним команду:

Следующим шагом добавим по одному тестовому файлу в наш пакет. Для удобства нашего примера сделаем все тесты похожими. На примере first файл с тестом будет выглядеть следующим образом:

Также нам необходимо добавить скрипт для запуска тестов в package.json каждой из наших библиотек:

В итоге package.json из корневой директории будет выглядеть следующим образом:

Теперь, чтобы запустить тесты нам достаточно из корня нашего проекта выполнить команду:

lerna js что это

Обновление версий с Lerna

В итоге наш корневой package.json будет выглядеть следующим образом:

Запустим скрипт обновления версии:

Далее нам будет предложено выбрать версию, на которую мы хотим перейти:

lerna js что это

Кликнув Enter мы получаем список пакетов, в которых обновлена версия.

lerna js что это

Подтверждаем обновление вводом y и мы получаем сообщение об успешном обновлении.

lerna js что это

Если попробовать выполнить команду git status, мы получим сообщение nothing to commit, working tree clean, т.к. lerna version не только обновляет версии пакетов, но и затем создаёт git коммит и тег с указанием новой версии (v2.0.0 в нашем случае).

Заключение

Приведенные примеры показывают одну сотую всех возможностей, которыми обладает lerna в связке с yarn workspaces. К сожалению, пока я не находил подробных инструкций по работе с монорепозиториями на русском языке, поэтому можем считать, что начало положено!

Источник

Множество JS-пакетов в одном репозитории

lerna js что это

Хабрадевелоперам, привет! Не так давно мы начали разрабатывать комплексный проект, у которого есть или планируется несколько видов фронт-енда, множество сервисов бэк-енда, интерфейс командной строки, демоны и много ещё чего. У всего этого в свою очередь есть шареный код, а совершенно новые приложения должно быть возможным собирать из имеющихся кирпичиков простым и понятным образом.

Если не занудствовать с терминологией, мы делаем платформу. Платформу для визуального программирования под DIY-электронику.

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

Что было вначале

Начиналось всё довольно традиционно. Наш репозиторий выглядел примерно так:

Имевшие дело со стеком React + Redux моментально узнают шаблон. В src/ лежат исходники фронт-енда, по команде они собираются Webpack’ом в dist/ откуда фронт-енд можно сервить, как простую статику.

Расширяемся

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

Пришло время делиться. В этот момент встал выбор перед двумя общепринятыми подходами разделения.

Rails-подход хорош тем, что слои чётко очерчены. Структура «пакетов» регламентирована и не провоцирует на изобретательство.

Но в этом кроется и проблема. Хотим мы, допустим, теперь CLI-интерфейс. React для утилит командной строки имеет не много смысла: слои components и containers не нужны. Зато нужно куда-то положить модули для красивого вывода в терминал, для парсинга аргументов и т.п. Для этого слоёв нет, придётся добавить только для CLI.

Ну и самое главное: не существует простого способа выдрать какой-то «пакет» из кодовой базы, сказать, что теперь это нечто самостоятельное, скинуть на дискетку и отправить почтой.

Поэтому мы остановились на pod-концепции.

Пути наверх

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

В этом есть что-то противоречивое. Пакеты хоть и разнесены по директориям, сохраняется строгое предположение об их размещении. Количество «точечек» варьируется в зависимости от вложенности модуля, который импортирует. Кроме прочего это ещё и затрудняет рефакторинг.

Хочется как с библиотеками: начинать импорт с названия библиотеки, и чтоб кто-нибудь за нас разобрался, где эту библиотеку брать:

Core превратился xod-core, чтобы исключить возможность конфликтов со сторонними библиотеками в случае использования простых названия. XOD — это название проекта, который мы делаем.

Однако при динамичной разработке жонглирование десятком репозиториев с npm install, build, publish, npm link, git pull, git push даже по ощущениям выглядит адово. Нужно как-то оставить всё в одном репозитории.

Покамест рефакторим структуру, явно выделяя пакеты:

Линковка

По идее для подобных сценариев есть npm link, но элементарно правильно навести все линки на новой машине, воспроизвести структуру проекта уже не просто: npm link — не stateless. А всё делается простоты ради. Поэтому нет, спасибо.

Ура, независимо от положения импортирующего модуля мы можем делать:

Симлинки можно совершенно спокойно хранить в Git-репозитории. И пока среди разработчиков не встречается Windows, всё будет хорошо работать: код сразу готов к сборке после клона.

Доворачивание до пакета

То, что получилось, пока ещё не является полноценными JS-пакетами. Для того, чтобы пакет мог быть залит на NPM и на равных со всеми правами использоваться в сторонних проектах, нужно каждый снабдить собственным package.json и прописать его единоличные зависимости. Сейчас же у нас единственное описание мега-пакета находится в корне. Туда же свалены все зависимости всех пакетов. Исправляем:

История с симлинками продолжает работать, как работала, зато каждый пакет получил собственную мета-информацию, собственные зависимости, нужные только ему. Структура стала управляемее.

И так для каждого действия. Немного неуклюже, но работает.

Бестолковые билды

Проблема такой структуры всплыла довольно быстро. То, что каждый пакет стал обладать собственными зависимостями с академической точки зрения хорошо, но с практической привело к тому, что одни и те же зависимости стали устанавливаться по нескольку раз. На один только make install уходило под 10 минут.

Львиную долю времени отъедала установка Webpack, Babel и их друзей.

Дополнительно, при билде одни и те же исходные файлы транспилировались/паковались по нескольку раз: по разу на собираемый пакет. Не продуктивно.

При таком подходе симлинки между пакетами достаточно перенавести с src/ на dist/ и чуть подправить конфиги Webpack’а, чтобы он не процессил «чужие» исходники.

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

В корень переехали все инструменты из dev-dependencies: Webpack, Babel, Mocha, ESLint.

Эта пара мер вернула полную сборку и проверку на CI-сервере в три минуты. Соответсвтенно и на localhost’е дела пошли бодрее.

Lerna

Пока мы перемещали директории с пакетами туда-сюда, я наткнулся на Lerna. Это инструмент, который был в своё время вычленен из Babel’а и как раз помогает держать множество пакетов в одном репозитории. Так сделано, конечно же, и в самом Babel’е.

Среди полезностей Lerna позволяет запустить npm-команду внутри каждого пакета, бампнуть версию каждого пакета, а главное она позволяет сделать так называемый bootstraping.

Всё бы хорошо, только Lerna не совместима с текущей структурой по двум статьям:

Первая проблема решается тривиально. Со второй всё сложнее.

Дело в том, что мы не сможем писать:

Придётся всюду писать:

В этом есть что-то противоестественное. А что, если какой-нибудь пакет захочет билдиться для нескольких видов таргетов, и в его dist/ появятся соответствующие поддиректории? Придётся переписывать абсолютно все пути импорта. Плохо-плохо.

Внутри, условно, хоть трава не расти, а внаружу, будь добр: хороший и красивый API.

В итоге все наши многочисленные импорты вида:

превратились в элегантные:

Сами пакеты, в своих корневых index.js просто реэкспортируют необходимые символы внаружу.

Заключение

Как итог, мы получили довольно приятную структуру, с которой комфортно работать и чувствуется, что она выдержит ещё не одну тысячу коммитов.

Не претендую на то, что представленный подход «правильный». Так сложилось у нас и сложилось оно на основе эволюционных изменений, которые проходил проект. Если кому-то окажутся полезными изложенные мысли, я буду рад 😉

Источник

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

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