[Node.JS, Open source, Системы сборки] Сборка сложных Node.js проектов утилитой run-z
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
Есть несколько десятков взаимосвязанных пакетов в рабочем дереве (Yarn Workspaces).
Надо собирать несколько из них. Часто, быстро, и в правильном порядке.
Существующие инструменты либо собирают всё сразу и долго, либо собирают в произвольном порядке, что некорректно и не всегда возможно.
Решение — run-z
Так выглядит сборка
SPL
Установка
npm install run-z --save-dev # Используя NPM
yarn add run-z --dev # Используя Yarn
Теперь в package.json можно добавлять задачи
{
"scripts": {
"all": "run-z build lint,test",
"build": "run-z --then tsc -p .",
"clean": "run-z --then shx rm -rf ./dist",
"lint": "run-z --then eslint .",
"test": "run-z --then jest",
"z": "run-z"
}
}
И запускать их
npm run all # Запуск одной задачи, используя NPM
yarn all # Запуск одной задачи, используя Yarn
yarn clean build # Запуск нескольких задач, используя Yarn
npm run clean -- build # Запуск нескольких задач, используя NPM
npm run z -- clean build # Запуск через пустую задачу `z`
Рекомендую всегда добавлять пустую задачу, например z. Она позволит передать дополнительные параметры в run-z, а не в npm или yarn. Например, вот так можно вызвать справку:
yarn z --help
npm run z -- --help
Как видите, синтаксис вызова у Yarn проще, чем у NPM.
Ниже в тексте я буду использовать Yarn в примерах.
Задачи
Задачи записываются как обычные сценарии в разделе scripts файла package.json. Если такой сценарий запускает команду run-z, то последняя трактует все сценарии как свои задачи и может запустить сразу несколько.
Если перечислить несколько задач в командной строке run-z, то выполнение каждой из них станет предварительным условием для следующей:
run-z prerequisite1 prerequisite2 --then node ./my-script.js --arg
Такая задача запустит сначала prereqiusite1, затем, дождавшись её завершения — prerequisite2, и только по её окончании — запустит сценарий node ./my-script.js --arg.
Поддерживаются четыре типа задач:
- Команда содержит опцию --then. Всё, что следует за этой опцией — это команда с аргументами, которая будет выполнена.
- Сценарий NPM — это любой сценарий в разделе scripts файла package.json, отличный от run-z. run-z запускает такие сценарии через npm run или yarn run.
- Группа содержит список задач для запуска, но не содержит команды. Список задач может быть пустым.
- Неизвестная задача создаётся, если не соответствует ни одному сценарию в package.json. При попытке её запуска возникнет ошибка. Но задачи можно пропускать, тогда никакой ошибки не будет.
Параметры выполнения задач
Можно передавать дополнительные параметры в вызываемые задачи. Для этого предназначен особый синтаксис:
run-z test/--ci/--runInBand # Передача `--ci` и `--runInBand`
# в команду или сценарий NPM,
# запущенный задачей `test`.
run-z test //--ci --runInBand// # Несколько параметров сразу.
Отдельный синтаксис нужен, чтобы передавать параметры конкретной задаче, а не команде run-z. Впрочем, неопознанные параметры будут переданы задаче и так, без знака /.
Одиночные параметры отделяются от задачи одиночным знаком /. Перед ним можно добавлять пробелы.
Много параметров можно заключать между ограничителями из нескольких (двух или более) знаков /.
Атрибуты задач
Атрибуты — это пары ключ/значение, которые можно передавать задачам примерно так же, как и параметры:
run-z test/attribute=value # Атрибут `attribute` со значением `value`
# для задачи `test`
run-z build test attribute=value # Атрибут `attribute` со значением `value`
# для самой задачи и всех предварительных задач.
run-z test/=if-present # Сокращённо `if-present=on`.
Атрибуты пока не очень полезны, но уже могут быть использованы в некоторых случаях:
- Когда установлен атрибут if-present для задачи, отсутствующей в package.json, то попытки выполнить такую задачу не будет предпринято и ошибка не возникнет.
Может быть полезно, когда задача выполняется в нескольких пакетах одновременно, но в некоторых пакетах такая задача не определена.
- Когда для задачи установлен атрибут skip, то выполняться такая задача не будет.
Дополнения к задачам
run-z запускает каждую задачу только раз, сколько бы раз она ни была вызвана. Даже если одна задача требуется для выполнения нескольких других. И каждый раз, как задача вызывается, ей можно передавать параметры и атрибуты. Параметры таких вызовов объединяются.
Также есть особый синтаксис вызова задачи, называемый дополнением. Если перед именем задачи поставить знак +, то параметры в задачу будут переданы, но вот выполнение задачи инициировано не будет.
Это можно использовать, например, для задания параметров задачи по умолчанию. Так что с таким package.json:
{
"scripts": {
"test": "run-z +z jest",
"z": "run-z +test/--runInBand"
}
}
при исполнении задачи test, jest всегда будет вызываться с опцией --runInBand.
Параллельное и последовательное выполнение
Любые две задачи выполняются последовательно, если только им не разрешено выполняться параллельно.
Запятая между именами задач разрешает их параллельное выполнение.
Например, задача
run-z clean build lint,test
Выполнит lint и test параллельно, но лишь когда build завершится. А задача build начнёт выполняться, только когда завершится clean.
Также можно выполнять команды параллельно с другими задачами. Для этого достаточно опцию --then заменить на опцию --and:
run-z copy-assets --and tsc -p . # Копирует файлы и компилирует
# TypeScript одновременно.
Число одновременно выполняемых задач ограничено. По умолчанию — числом процессоров. Но можно это исправить опцией --max-jobs (сокращённо -j):
run-z build,lint,test -j2 # Только две задачи одновременно.
run-z build,lint,test -max-jobs 1 # Отключает параллельное выполнение.
run-z build,lint,test -j0 # Убирает ограничение.
Пакетное выполнение
Можно выполнить задачу в другом пакете:
run-z ../package1 build test . build test
Эта задача выполнит build и test в пакете из директории ../package1, а затем — в текущем.
Опции командной строки ., .., а также начинающиеся с ./ и ../ — это URL указывающие на директории с пакетами. Задачи, перечисленные после них, будут найдены и выполнены в целевом пакете.
В общем случае такие опции — селекторы — могут выбрать сразу несколько пакетов. Тогда задача с одним и тем же именем будет выполнена во всех. Партией:
run-z ./packages// build # Выполнит `build` в каждом пакете
# непосредственно внутри директории `./packages`.
run-z ./packages/// build # Выполнит `build` в директории `./packages`
# и в каждом пакете найденном как угодно глубже.
// выбирает непосредственно вложенные директории. /// выбирает директорию и её поддиректории на любую глубину. Скрытые директории и директории без файла package.json игнорируются.
Можно указать сразу несколько селекторов. Результаты выборок будет объединены:
run-z ./3rd-party// ./packages// build # Выполнит `build` в каждом пакете
# из директорий `./3rd-party`
# и `./packages`.
Порядок выполнения задач из партии определяется зависимостями между пакетами. То есть сначала задача выполняется для зависимости, а затем — для зависящего от него пакета. Задачи в независимых пакетах выполняются параллельно.
Можно разрешить всем задачам в партии выполняться параллельно опцией --batch-parallel, сокращённо --bap:
run-z --batch-parallel ./packages// lint # Выполнит `lint` в каждом пакете
# внутри директории `./packages
# параллельно.
Подзадачи
Задача типа "группа" не только выполняет перечисленные задачи. Она также может быть использована для запуска произвольных задач в выбранных пакетах.
Для этого группе можно передать параметры вызова. И первым параметром будет имя (под-)задачи, которую нужно выполнить. Остальные параметры будут переданы уже в эту подзадачу.
Так что с таким package.json:
{
"scripts": {
"each": "run-z ./3rd-party// ./packages//"
}
}
можно выполнить задачи партией внутри директорий 3rd-party/ и packages/:
yarn each /build each /test # Выполнит `build`, а затем `test` во всех пакетах.
Именованные партии задач
Для удобства работы в рабочем дереве (например Yarn Workspaces) партии задач можно именовать.
Допустим, есть корневой пакет с таким package.json:
{
"scripts": {
"all/*": "run-z ./packages//",
"z": "run-z"
}
}
Здесь сценарий с именем "all/*" — это описание именованной партии. С таким описанием становится возможным пакетное выполнение задач как находясь в корне, так и находясь во вложенных директориях:
yarn z build --all # Выполняет `build` во всех пакетах.
Когда указана опция --all, run-z ищет самый верхний пакет, содержащий именованные партии задач, и выполняет задачи в этих партиях.
Имя именованной партии может быть "имя_партии/имя_задачи". Такая именованная партия используется вместо "имя_партии/*", когда выполняется задача "имя_задачи". Это полезно, например, когда нужно передать дополнительные опции в конкретную задачу:
{
"scripts": {
"all/*": "run-z ./packages//",
"all/test": "run-z ./packages// +test/--runInBand",
"z": "run-z"
}
}
yarn z build --all # Выполняет `build` партией.
yarn z test --all # Выполняет `test` партией с опцией `--runInBand`.
Граф зависимостей
Именованные партии позволяют выполнять задачи в подмножестве графа зависимостей текущего пакета:
yarn build --with-deps # Выполняет `build` в зависимостях
# и в самом пакете.
yarn build --only-deps # Выполняет `build` только в зависимостях.
yarn build --with-dependants # Выполняет `build` в пакете,
# а затем во всех зависимых пакетах.
yarn build --only-dependants # Выполняет `build` только в зависимых пакетах.
Сравнение с npm-run-all
npm-run-all — это весьма популярный инструмент для сборки. Прежде я использовал именно его и могу сравнивать.
Вот так выглядели сценарии сборки типичного проекта, использующего TypeScript, Rollup, ESLint и Jest:
{
"scripts": {
"all": "run-p --aggregate-output build:all "test {@}" --",
"build": "rollup --config ./rollup.config.js",
"build:all": "run-p --aggregate-output rebuild lint",
"ci:all": "run-p --aggregate-output build:all ci:test",
"ci:test": "jest --ci --runInBand",
"clean": "shx rm -rf d.ts dist target",
"lint": "eslint .",
"rebuild": "run-s clean build",
"test": "jest",
}
}
run-p здесь выполняет перечисленные задачи параллельно. run-s — последовательно.
И вот как это выглядит теперь:
{
"scripts": {
"all": "run-z build,lint,test",
"build": "run-z +z rollup --config ./rollup.config.js",
"ci:all": "run-z all +test/--ci/--runInBand",
"clean": "run-z +z --then shx rm -rf d.ts dist target",
"lint": "run-z +z --then eslint .",
"test": "run-z +z --then jest",
"z": "run-z +build,+doc,+lint,+test"
}
}
- Вспомогательная задача "ci:test" для запуска тестов в окружении CI больше не нужна. Все необходимые параметры можно передать прямо в задачу "test".
- Вспомогательная задача "build:all" была нужна только чтобы выполнять задачи параллельно. Теперь их можно перечислить через запятую.
- Задача "rebuild" тоже стала не нужна, поскольку можно вызывать несколько задач прямо из командной строки: yarn clean build.
- Появилась задача "z". Она не просто для удобства, а ещё и разрешает параллельное выполнение некоторых задач. Так что yarn build lint test выполнит эти задачи параллельно. Не нужно каждый раз вспоминать, где поставить запятую.
- Все сценарии теперь начинаются с run-z. Это необязательно для простых сценариев, но даёт возможность применить настройки по умолчанию, а также запустить несколько задач сразу, например yarn clean build.
- Немаловажно также то, что run-z запускается единожды, сколько бы заданий не требовалось выполнить. А run-p или run-s запускаются каждым сценарием. Запуск V8 совсем не бесплатен.
Планы на будущее
В ближайших планах — добавить поддержку расширений. Основной замысел в том, чтобы уметь запускать не только внешние команды, но и использовать thread_workers. Хотя бы вместо некоторых команд. Это позволит как сэкономить ресурсы, так и существенно ускорить сборку. Особенно для быстрых утилит типа shx rm. Ведь последняя тратит на порядки больше времени на свой запуск, чем, собственно, на работу.
===========
Источник:
habr.com
===========
Похожие новости:
- [Программирование, Системы сборки] Make на мыло, redo сила
- [Angular, Open source, Rust, Визуализация данных, Отладка] Обновления в Chipmunk
- [Kotlin, Голосовые интерфейсы, Разработка мобильных приложений, Разработка под Android] Как встроить голосового помощника в любое мобильное приложение. Разбираем на примере Habitica
- [JavaScript, Node.JS, Разработка веб-сайтов] Безопасность npm-проектов, часть 1
- [Open source] Обзор OpenStack Neutron PTG июнь 2020
- [Open source, *nix] FOSS News №31 – дайджест новостей свободного и открытого ПО за 24-30 августа 2020 года
- [Open source, Программирование, Системное программирование, Компиляторы, Rust] Rust 1.46.0: track_caller и улучшения const fn (перевод)
- [JavaScript, Node.JS, Open source, Разработка веб-сайтов] Сладкая жизнь, или Создание веб-приложения без написания кода
- [Open source, Системное администрирование, IT-инфраструктура, Визуализация данных] Grafana+Zabbix: Визуализация работы производственной линии
- [Open source, Разработка под Linux, Учебный процесс в IT, Облачные сервисы] Red Hat Flatpak, DevNation Day, шпаргалка по программированию на Cи и пять вебинаров на русском
Теги для поиска: #_node.js, #_open_source, #_sistemy_sborki (Системы сборки), #_node.js_npmscripts, #_node.js, #_open_source, #_sistemy_sborki (
Системы сборки
)
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 22:46
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
Есть несколько десятков взаимосвязанных пакетов в рабочем дереве (Yarn Workspaces). Надо собирать несколько из них. Часто, быстро, и в правильном порядке. Существующие инструменты либо собирают всё сразу и долго, либо собирают в произвольном порядке, что некорректно и не всегда возможно. Решение — run-z Так выглядит сборкаSPLУстановка npm install run-z --save-dev # Используя NPM
yarn add run-z --dev # Используя Yarn Теперь в package.json можно добавлять задачи {
"scripts": { "all": "run-z build lint,test", "build": "run-z --then tsc -p .", "clean": "run-z --then shx rm -rf ./dist", "lint": "run-z --then eslint .", "test": "run-z --then jest", "z": "run-z" } } И запускать их npm run all # Запуск одной задачи, используя NPM
yarn all # Запуск одной задачи, используя Yarn yarn clean build # Запуск нескольких задач, используя Yarn npm run clean -- build # Запуск нескольких задач, используя NPM npm run z -- clean build # Запуск через пустую задачу `z` Рекомендую всегда добавлять пустую задачу, например z. Она позволит передать дополнительные параметры в run-z, а не в npm или yarn. Например, вот так можно вызвать справку: yarn z --help
npm run z -- --help Как видите, синтаксис вызова у Yarn проще, чем у NPM. Ниже в тексте я буду использовать Yarn в примерах. Задачи Задачи записываются как обычные сценарии в разделе scripts файла package.json. Если такой сценарий запускает команду run-z, то последняя трактует все сценарии как свои задачи и может запустить сразу несколько. Если перечислить несколько задач в командной строке run-z, то выполнение каждой из них станет предварительным условием для следующей: run-z prerequisite1 prerequisite2 --then node ./my-script.js --arg
Такая задача запустит сначала prereqiusite1, затем, дождавшись её завершения — prerequisite2, и только по её окончании — запустит сценарий node ./my-script.js --arg. Поддерживаются четыре типа задач:
Параметры выполнения задач Можно передавать дополнительные параметры в вызываемые задачи. Для этого предназначен особый синтаксис: run-z test/--ci/--runInBand # Передача `--ci` и `--runInBand`
# в команду или сценарий NPM, # запущенный задачей `test`. run-z test //--ci --runInBand// # Несколько параметров сразу. Отдельный синтаксис нужен, чтобы передавать параметры конкретной задаче, а не команде run-z. Впрочем, неопознанные параметры будут переданы задаче и так, без знака /. Одиночные параметры отделяются от задачи одиночным знаком /. Перед ним можно добавлять пробелы. Много параметров можно заключать между ограничителями из нескольких (двух или более) знаков /. Атрибуты задач Атрибуты — это пары ключ/значение, которые можно передавать задачам примерно так же, как и параметры: run-z test/attribute=value # Атрибут `attribute` со значением `value`
# для задачи `test` run-z build test attribute=value # Атрибут `attribute` со значением `value` # для самой задачи и всех предварительных задач. run-z test/=if-present # Сокращённо `if-present=on`. Атрибуты пока не очень полезны, но уже могут быть использованы в некоторых случаях:
Дополнения к задачам run-z запускает каждую задачу только раз, сколько бы раз она ни была вызвана. Даже если одна задача требуется для выполнения нескольких других. И каждый раз, как задача вызывается, ей можно передавать параметры и атрибуты. Параметры таких вызовов объединяются. Также есть особый синтаксис вызова задачи, называемый дополнением. Если перед именем задачи поставить знак +, то параметры в задачу будут переданы, но вот выполнение задачи инициировано не будет. Это можно использовать, например, для задания параметров задачи по умолчанию. Так что с таким package.json: {
"scripts": { "test": "run-z +z jest", "z": "run-z +test/--runInBand" } } при исполнении задачи test, jest всегда будет вызываться с опцией --runInBand. Параллельное и последовательное выполнение Любые две задачи выполняются последовательно, если только им не разрешено выполняться параллельно. Запятая между именами задач разрешает их параллельное выполнение. Например, задача run-z clean build lint,test
Выполнит lint и test параллельно, но лишь когда build завершится. А задача build начнёт выполняться, только когда завершится clean. Также можно выполнять команды параллельно с другими задачами. Для этого достаточно опцию --then заменить на опцию --and: run-z copy-assets --and tsc -p . # Копирует файлы и компилирует
# TypeScript одновременно. Число одновременно выполняемых задач ограничено. По умолчанию — числом процессоров. Но можно это исправить опцией --max-jobs (сокращённо -j): run-z build,lint,test -j2 # Только две задачи одновременно.
run-z build,lint,test -max-jobs 1 # Отключает параллельное выполнение. run-z build,lint,test -j0 # Убирает ограничение. Пакетное выполнение Можно выполнить задачу в другом пакете: run-z ../package1 build test . build test
Эта задача выполнит build и test в пакете из директории ../package1, а затем — в текущем. Опции командной строки ., .., а также начинающиеся с ./ и ../ — это URL указывающие на директории с пакетами. Задачи, перечисленные после них, будут найдены и выполнены в целевом пакете. В общем случае такие опции — селекторы — могут выбрать сразу несколько пакетов. Тогда задача с одним и тем же именем будет выполнена во всех. Партией: run-z ./packages// build # Выполнит `build` в каждом пакете
# непосредственно внутри директории `./packages`. run-z ./packages/// build # Выполнит `build` в директории `./packages` # и в каждом пакете найденном как угодно глубже. // выбирает непосредственно вложенные директории. /// выбирает директорию и её поддиректории на любую глубину. Скрытые директории и директории без файла package.json игнорируются. Можно указать сразу несколько селекторов. Результаты выборок будет объединены: run-z ./3rd-party// ./packages// build # Выполнит `build` в каждом пакете
# из директорий `./3rd-party` # и `./packages`. Порядок выполнения задач из партии определяется зависимостями между пакетами. То есть сначала задача выполняется для зависимости, а затем — для зависящего от него пакета. Задачи в независимых пакетах выполняются параллельно. Можно разрешить всем задачам в партии выполняться параллельно опцией --batch-parallel, сокращённо --bap: run-z --batch-parallel ./packages// lint # Выполнит `lint` в каждом пакете
# внутри директории `./packages # параллельно. Подзадачи Задача типа "группа" не только выполняет перечисленные задачи. Она также может быть использована для запуска произвольных задач в выбранных пакетах. Для этого группе можно передать параметры вызова. И первым параметром будет имя (под-)задачи, которую нужно выполнить. Остальные параметры будут переданы уже в эту подзадачу. Так что с таким package.json: {
"scripts": { "each": "run-z ./3rd-party// ./packages//" } } можно выполнить задачи партией внутри директорий 3rd-party/ и packages/: yarn each /build each /test # Выполнит `build`, а затем `test` во всех пакетах.
Именованные партии задач Для удобства работы в рабочем дереве (например Yarn Workspaces) партии задач можно именовать. Допустим, есть корневой пакет с таким package.json: {
"scripts": { "all/*": "run-z ./packages//", "z": "run-z" } } Здесь сценарий с именем "all/*" — это описание именованной партии. С таким описанием становится возможным пакетное выполнение задач как находясь в корне, так и находясь во вложенных директориях: yarn z build --all # Выполняет `build` во всех пакетах.
Когда указана опция --all, run-z ищет самый верхний пакет, содержащий именованные партии задач, и выполняет задачи в этих партиях. Имя именованной партии может быть "имя_партии/имя_задачи". Такая именованная партия используется вместо "имя_партии/*", когда выполняется задача "имя_задачи". Это полезно, например, когда нужно передать дополнительные опции в конкретную задачу: {
"scripts": { "all/*": "run-z ./packages//", "all/test": "run-z ./packages// +test/--runInBand", "z": "run-z" } } yarn z build --all # Выполняет `build` партией.
yarn z test --all # Выполняет `test` партией с опцией `--runInBand`. Граф зависимостей Именованные партии позволяют выполнять задачи в подмножестве графа зависимостей текущего пакета: yarn build --with-deps # Выполняет `build` в зависимостях
# и в самом пакете. yarn build --only-deps # Выполняет `build` только в зависимостях. yarn build --with-dependants # Выполняет `build` в пакете, # а затем во всех зависимых пакетах. yarn build --only-dependants # Выполняет `build` только в зависимых пакетах. Сравнение с npm-run-all npm-run-all — это весьма популярный инструмент для сборки. Прежде я использовал именно его и могу сравнивать. Вот так выглядели сценарии сборки типичного проекта, использующего TypeScript, Rollup, ESLint и Jest: {
"scripts": { "all": "run-p --aggregate-output build:all "test {@}" --", "build": "rollup --config ./rollup.config.js", "build:all": "run-p --aggregate-output rebuild lint", "ci:all": "run-p --aggregate-output build:all ci:test", "ci:test": "jest --ci --runInBand", "clean": "shx rm -rf d.ts dist target", "lint": "eslint .", "rebuild": "run-s clean build", "test": "jest", } } run-p здесь выполняет перечисленные задачи параллельно. run-s — последовательно. И вот как это выглядит теперь: {
"scripts": { "all": "run-z build,lint,test", "build": "run-z +z rollup --config ./rollup.config.js", "ci:all": "run-z all +test/--ci/--runInBand", "clean": "run-z +z --then shx rm -rf d.ts dist target", "lint": "run-z +z --then eslint .", "test": "run-z +z --then jest", "z": "run-z +build,+doc,+lint,+test" } }
Планы на будущее В ближайших планах — добавить поддержку расширений. Основной замысел в том, чтобы уметь запускать не только внешние команды, но и использовать thread_workers. Хотя бы вместо некоторых команд. Это позволит как сэкономить ресурсы, так и существенно ускорить сборку. Особенно для быстрых утилит типа shx rm. Ведь последняя тратит на порядки больше времени на свой запуск, чем, собственно, на работу. =========== Источник: habr.com =========== Похожие новости:
Системы сборки ) |
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 22:46
Часовой пояс: UTC + 5