Мы ступили на эти ямы Монорепо для вас!

внешний интерфейс
Мы ступили на эти ямы Монорепо для вас!

предисловие

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

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

Его можно свести к следующим трем пунктам:

  • Через пакет в репозитории ссылок на рабочее пространство пряжи
  • Используйте пряжу в качестве менеджера пакетов для управления зависимостями в вашем проекте.
  • Соберите пакеты, от которых он зависит, с помощью lerna в соответствии с зависимостями до сборки приложения.

существующие проблемы

непоследовательная команда

Есть три команды

  1. yarn
  2. yarn workspace
  3. lerna

У новичков легко вызвать недопонимание, а некоторые команды имеют дублирующие друг друга функции.

медленное высвобождение

monorepo1

Если нам нужно опубликовать app1, мы

  1. Будут установлены полные установочные зависимости, app1, app2, app3 и зависимости от package1 до package6;
  2. Все пакеты собраны, а не только пакеты1 и пакет2, от которых зависит app1.

Phantom dependencies

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

monorepo-2

Поскольку версионность фантомных зависимостей не может быть гарантирована, это вносит неконтролируемые риски в работу программы. Приложение зависит от lib-a, а lib-a зависит от lib-x. Из-за обновления зависимостей мы можем напрямую ссылаться на lib-x в приложении, что ненадежно. Можем ли мы ссылаться на lib-x и на какую версию lib -x полностью зависит от разработчика lib-a.

NPM doppelgnger

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

Предположим, что существуют следующие зависимости

monorepo-3

Есть два возможных результата окончательной установки зависимостей:

  1. lib-x@^1 * 1 копия, lib-x@^2 * 2 копии
  2. lib-x@^2 * 1 копия, lib-x@^1 * 2 копии

В итоге локально будет установлено 3 копии lib-x, а при упаковке будет три экземпляра lib-x.Если для lib-x требуется синглтон, это может вызвать проблемы.

Yarn duplicate

Дублирование пряжи и решения

Предположим, что существуют следующие зависимости

monorepo-4

Когда (p)npm устанавливается в тот же модуль, оценивается, соответствует ли версия установленного модуля диапазону версий нового модуля. Если он соответствует, он будет пропущен. Если он не соответствует, модуль будет быть установлен под node_modules текущего модуля. То есть lib-a будет повторно использовать зависимости приложения.lib-b@1.1.0.

Однако при использовании Yarn v1 в качестве менеджера пакетов lib-a будет устанавливаться отдельно.lib-b@1.2.0.

риск одноранговых зависимостей

Обновление зависимостей Yarn может привести к ошибкам в сценарии peerDependencies.

  1. зависимости app1A@1.0.0
  2. зависимости приложения2B@2.0.0
  3. B@2.0.0БудуA@2.0.0как peerDependency, поэтому app2 также должен быть установленA@2.0.0

Если вы забыли установить app2A@2.0.0, то структура такая

--apps
    --app1
    --app2
--node_modules
    --A@1.0.0
    --B@2.0.0

В настоящее времяB@2.0.0неверно цитироватьA@1.0.0.

Отсутствует справочная спецификация пакета

На данный момент в проекте есть три эталонных метода:

  1. Ссылка на исходный код: используйте ссылку на имя пакета. Сценарий сборки хост-проекта необходимо настроить для включения пакета в процесс сборки. Как и при прямой публикации исходного пакета TypeScript, проекты, которые ссылаются на пакет, должны быть адаптированы.
  2. Ссылка на источник: используйте ссылки на пути к файлам. Можно понять, что «исходные файлы, размещенные за пределами собственного src», являются частью исходного кода основного проекта, а не пакета. Хост должен предоставить все зависимости, что обеспечивает повторное использование между проектами в соответствии с предпосылкой улучшения зависимостей Yarn, но есть большие риски.
  3. Цитирование продукта. После того, как упаковка завершена, продукт напрямую упоминается по имени упаковки.

Неопределенность эталонной версии пакета

Предполагая, что package1 в монорепозитории опубликован в репозитории npm, как app1 в монорепозитории написать номер версии, который ссылается на package1 в package.json?

package1/packag.json

{
  "name": "package1",
  "version": "1.0.0"
}

app1/package.json

{
  "name": "app1",
  "dependencies": {
    "package-1": "?" // 这里的版本号应该怎么写?`*` or `1.0.0`
  }
}

Имея дело с взаимными ссылками проектов в Monorepo, Yarn будет принимать следующие решения:

  1. Определите, есть ли в текущем монорепозитории package1, который соответствует версии, требуемой приложением app1;
  2. Если он существует, выполните операцию ссылки, и app1 напрямую использует локальный package1;
  3. Если он не существует, извлеките версию package1 из удаленного репозитория npm для использования app1.

Особо следует отметить:*не может соответствоватьprerelease 版本👉Workspace package with prerelease version and wildcard dep version #6719.

Предположим, существует следующий сценарий:

  1. package1 ранее был выпущен1.0.0версии, на данный момент удаленный репозиторий совпадает с кодом в локальном монорепозитории;
  2. Одноклассник по продукту выдвинул требование, которое обслуживает только внутреннее приложение Monorepo;
  3. пакет1 в1.0.0Версия повторяется, номер версии менять не нужно;
  4. Yarn считает, что версия package1 в Monorepo соответствует версии, требуемой приложением app1 (*или1.0.0);
  5. app1 успешно использует последние функции package1.

Вплоть до наступления дня эта функция требования должна быть предоставлена ​​для использования внешним бизнес-партнерам.

  1. pacakge1 изменяет версию на1.0.0-beta.0и опубликовать его;
  2. Yarn считает, что версия пакета1 в текущем монорепозитории не соответствует версии, необходимой для приложения1;
  3. тянуть с пультаpackage1@1.0.0для использования app1;
  4. удаленныйpackage@1.0.0отстал от локального, ранее использовавшегося app1package@1.0.0слишком;
  5. Подготовьте уведомление об инциденте и обзор.

Эта неопределенность приводит к частым возражениям при упоминании таких пакетов: я ссылаюсь на локальную или удаленную версию? Почему иногда это локальная версия, а иногда удаленная версия? Я хочу использовать самое последнее содержимое package1 и должен постоянно соответствовать номеру версии package1, так почему я должен использовать Monorepo?

Конфликт пряжи.lock

(p) npm поддерживает автоматическое разрешение конфликтов файлов блокировки, пряжа должна обрабатываться вручную, в больших сценариях Monorepo конфликты пряжи. блокировка будут возникать почти при каждом слиянии веток.

  • Нет разрешения конфликтаyarn,yarn.lockОн будет недействителен напрямую, и все версии будут обновлены доpackage.jsonпоследнее, слишком рискованное, потерянноеlockfileимея в виду;
  • Часто происходит ручное разрешение конфликтовGit conflict with binary files, использовать толькоmasterпредставить и повторноyarn, процесс громоздкий.

Automatically resolve conflicts in lockfile · Issue #2036 · pnpm/pnpm

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

Рекомендуемое чтение:Дилемма node_modules

решение

pnpm

Fast, disk space efficient package packageManager

До npm@3 структура node_modules была чистой и предсказуемой, так как каждая зависимость в node_modules имела собственную папку node_modules со всеми ее зависимостями, указанными в package.json.

node_modules
└─ foo
   ├─ index.js
   ├─ package.json
   └─ node_modules
      └─ bar
         ├─ index.js
         └─ package.json

Но это приносит две серьезные проблемы:

  1. Если уровень зависимости слишком глубокий, под Windows будут проблемы;
  2. Когда один и тот же пакет используется как зависимость от многих других пакетов, он будет скопирован много раз.

Чтобы решить эти две проблемы, npm@3 переосмысливает структуру node_modules и вводит схему листов. Отсюда структура, с которой мы знакомы ниже.

node_modules
├─ foo
|  ├─ index.js
|  └─ package.json
└─ bar
   ├─ index.js
   └─ package.json

В отличие от npm@3, pnpm решает проблемы, с которыми сталкивается npm@2, иначе, чем тайлинг node_modules.

В созданной pnpm папке node_modules все Пакеты сгруппированы со своими зависимостями (изолированными), но не слишком глубоко (мягкие ссылки на реальные адреса снаружи).

-> - a symlink (or junction on Windows)

node_modules
├─ foo -> .registry.npmjs.org/foo/1.0.0/node_modules/foo
└─ .registry.npmjs.org
   ├─ foo/1.0.0/node_modules
   |  ├─ bar -> ../../bar/2.0.0/node_modules/bar
   |  └─ foo
   |     ├─ index.js
   |     └─ package.json
   └─ bar/2.0.0/node_modules
      └─ bar
         ├─ index.js
         └─ package.json
  1. Решите фантомные зависимости на основе неплоской структуры каталогов node_modules. Пакеты могут достигать только своих собственных зависимостей.
  2. Повторно используйте одну и ту же версию пакета через мягкую цепочку, чтобы избежать повторной упаковки (одна и та же версия) и решить двойник NPM (кстати, решить проблему занятости диска).

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

Rush

a scalable monorepo manager for the web

  1. Единая команда.

rush(x) xxxШаттл для снижения стоимости запуска новичков. В то же время Rush кромеrush addтак же какrushx xxxДругие команды необходимо запускать в указанном проекте, а другие команды являются глобальными командами, которые можно выполнять в любом каталоге проекта, что позволяет избежать проблемы частого переключения путей проекта в терминале.

monorepo-5

  1. Мощные возможности анализа зависимостей.

Многие команды в Rush поддерживают анализ зависимостей, например-t(к) параметру:

$ rush install -t @monorepo/app1

Эта команда установит только зависимости app1 и пакеты, от которых зависит app1, то есть установит зависимости по запросу.

$ rush build -t @monorepo/app1

Эта команда выполняет сценарии сборки app1 и пакеты, от которых зависит app1.

похожий, и-fПараметр (от) может заставить команду работать только с текущим пакетом и пакетами, которые зависят от этого пакета.

  1. Обеспечение согласованности версий зависимостей

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

Rush предоставляет множество возможностей для этого, например:rush check,rush add -p package-name -mтак же какensureConsistentVersions.

Заинтересованные студенты могут прочитать официальную документацию Rush, которая очень подробная и объясняет некоторые распространенные проблемы.

Справочная спецификация пакета

monorepo-12

Ссылка на продукт

В традиционном эталонном методе после завершения сборки приложение напрямую ссылается на продукт сборки пакета. Этап разработки может гарантировать сборку в реальном времени благодаря возможностям, предоставляемым инструментами сборки (например, tsc --watch).

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

исходный код

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

  • Преимущества: с помощью возможности горячего обновления приложения нет процесса создания продукта сборки, а скорость горячего обновления высокая.
  • Недостатки: Требуется адаптация приложения,aliasАдаптация громоздка;

Спецификации цитирования

  1. Для пакетов, используемых внутри проекта, называемых функциями, их нельзя выпускать во внешний мир.mainПоле устанавливается как запись исходного файла и настраивает веб-пакет проекта приложения и компилирует форму после выхода.
  2. Для пакетов, которые должны быть выпущены извне, ссылки на функции не должны и не допускаются.Должен быть процесс сборки.Если вам нужно использовать разработку исходного кода для увеличения скорости горячего обновления, вы можете добавить настраиваемое поле ввода , которому присваивается приоритет в конфигурации веб-пакета приложения.

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

Workspace protocol (workspace:)

Rush появился задолго до того, как PNPM и Yarn поддержали возможности Workspace. Подход Rush заключается в централизованной установке всех пакетов в папку common/temp, а затем Rush создает символические ссылки из каждого проекта на common/temp. По существу эквивалентен рабочему пространству PNPM.

включиPNPM workspaceВозможность использовать протокол workspace: для обеспечения того, чтобы версия, на которую ссылаются, была детерминированной, а пакет, на который ссылается этот протокол, будет использовать только содержимое в монорепозитории.

{
  "dependencies": {
    "foo": "workspace:*",
    "bar": "workspace:~",
    "qar": "workspace:^",
    "zoo": "workspace:^1.5.0"
  }
}

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

Если вы должны использовать удаленную версию, вам необходимоrush.jsonНастройте конкретные проекты в (добавитьcyclicDependencyProjects конфигурация), см.rush_json.

К счастью, в рабочей области PNPMworkspace:*Вы можете сравнить предварительную версию👉Local prerelease version of packages should be linked only if the range is *

журнал проблем

Monorepo Project Dependencies Duplicate

Эта проблема похожа на дубликат Yarn, упомянутый ранее, но не уникальна для Yarn.

Предположим, что существуют следующие зависимости (измените пример дубликата Yarn и поместите его в сцену Monorepo)

app1 и package1 принадлежат внутреннему проекту Monorepo.

monorepo-8

В проекте Rush(pnpm)/Yarn он будет установлен строго по версии, заявленной в package.json проекта в Монорепо, то есть установка app1lib-a@1.1.0, пакет1 установленlib-a@1.2.0.

На данный момент app1 упаковано, затемlib-a@1.1.0а такжеlib-a@1.2.0будет упакован.

У вас могут быть некоторые сюрпризы по поводу этого результата, но если подумать, это естественно.

Иными словами, весь монорепозиторий — это один большой виртуальный проект, и все наши проекты существуют как прямые зависимости от этого виртуального проекта.

{
  "name": "fake-project",
  "version": "1.0.0",
  "dependencies": {
    "@fake-project/app1": "1.0.0",
    "@fake-project/package1": "1.0.0"
  }
}

При установке зависимостей сначала загружается (p)npmПрямая зависимость, а затем скачатькосвенные зависимости, а при установке в этот же модуль определить, соответствует ли установленная версия модуля (прямые зависимости) диапазону версий нового модуля (косвенные зависимости), если да, то пропустить, если нет, установить под node_modules текущего модуль модуль.

Прямая зависимость lib-a приложений app1 и package1 является косвенной зависимостью fake-проекта, который не может соответствовать вышеуказанным условиям суждения, поэтому он устанавливается согласно версии, описанной в соответствующем package.json.

решение:Rush: Preferred versions

Раш можно указать вручную с помощьюpreferredVersionsспособ избежать дублирования двух совместимых версий. Здесь будет lib-a в монорепоpreferredVersionsУказание 1.2.0 эквивалентно прямой установке указанной версии модуля в виртуальном проекте в качестве прямой зависимости.

{
  "name": "fake-project",
  "version": "1.0.0",
  "dependencies": {
    "@fake-project/app1": "1.0.0",
    "@fake-project/package1": "1.0.0",
    "lib-a": "1.1.0"
  }
}

Для Yarn из-за существования дубликатов Yarn недопустимо устанавливать определенную версию lib-a в корневой каталог. Но есть еще два варианта решения проблемы:

  1. пройти черезyarn-deduplicateцелевая модификацияyarn.lock;
  2. использоватьresolutionsполе.过于粗暴,不像preferredVersionsНесовместимые версии разрешены, но не рекомендуются.

Необходимо помнить: устранение повторяющихся зависимостей в Yarn также должно выполняться пакет за пакетом и быть осторожным, чтобы сделать корабль тысячелетним.

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

prettier

Поскольку node_modules больше не существует в корневом каталоге, его необходимо установить для каждого проекта.prettierкак devDependency и напишите.prettierrc.jsдокумент.

По принципу лени вновь создается корневая директория.prettierrc.js(не зависит от каких-либо сторонних пакетов), устанавливается глобальноprettierрешить проблему.

eslint

Давайте сначала посмотрим на сцену, если вы используете ее в проектеeslint-config-react-app, помимо необходимости установкиeslint-config-react-app, вам также необходимо установить ряд подключаемых модулей peerDependencies.

monorepo-10

monorepo-11

Зачемeslint-config-react-appНе создавайте эту серию плагинов как зависимости, а как одноранговые зависимости? Пользователю не нужно заботиться о том, на какие плагины ссылаются в конфигурации по умолчанию.

Для конкретных обсуждений, пожалуйста, обратитесь к этому выпуску, который содержит обсуждения связанных вопросов:Support having plugins as dependencies in shareable config #3458.

В целом: Это связано со специфическим методом поиска плагинов eslint. проблемы, и пользователи могут самостоятельно установить peerDependencies.Эта проблема возникает.

Конечно, мы также нашли кое-что с открытым исходным кодом.eslintКонфигурация по умолчанию не требует установки peerDependencies, эти пресеты используют плоскую структуру node_modules пряжи и npm, то есть продвижение зависимостей, установленные пакеты продвигаются в корневой каталог node_modules, чтобы они могли нормально работать. Тем не менее, в монорепозитории на основе пряжи, когда зависимости сложные, могут быть случаи, когда плагин не может быть найден, и интересное совпадение, что он работает правильно.

В Раше зависимости нет (улучшается за счет повышения надежности), а ряд плагинов будет слишком громоздким, тогда можнопластырьспособ обхода.

git hooks

Часто используется в проектахhuskyрегистрpre-commitа такжеcommit-msgХук для проверки стиля кода и информации о коммите.

Очевидно, что в структуре проекта Rush корневая директория не имеет node_modules и не может использоваться напрямую.husky.

мы можем использоватьrush init-autoinstallerВозможность добиться того же эффекта, этот раздел в основном относится к официальной документацииInstalling Git hooksтак же какEnabling PrettierСодержание.

# 初始化一个名为 rush-lint 的 autoinstaller

$ rush init-autoinstaller --name rush-lint

$ cd common/autoinstallers/rush-lint

# 安装 lint 所需依赖

$ pnpm i @commitlint/cli @commitlint/config-conventional @microsoft/rush-lib eslint execa prettier lint-staged

# 更新 rush-lint 的 pnpm-lock.yaml

$ rush update-autoinstaller --name rush-lint

существуетrush-lintДобавить в каталогcommit-lint.jsтак же какcommitlint.config.js, содержание следующее

commit-lint.js

const path = require('path');
const fs = require('fs');
const execa = require('execa');

const gitPath = path.resolve(__dirname, '../../../.git');
const configPath = path.resolve(__dirname, './commitlint.config.js');
const commitlintBinPath = path.resolve(__dirname, './node_modules/.bin/commitlint');

if (!fs.existsSync(gitPath)) {
    console.error('no valid .git path');
    process.exit(1);
}

main();

async function main() {
    try {
        await execa('bash', [commitlintBinPath, '--config', configPath, '--cwd', path.dirname(gitPath), '--edit'], {
            stdio: 'inherit',
        });
    } catch (\_e) {
        process.exit(1);
    }
}

commitlint.config.js

const rushLib = require("@microsoft/rush-lib");

const rushConfiguration = rushLib.RushConfiguration.loadFromDefaultLocation();

const packageNames = [];
const packageDirNames = [];

rushConfiguration.projects.forEach((project) => {
  packageNames.push(project.packageName);
  const temp = project.projectFolder.split("/");
  const dirName = temp[temp.length - 1];
  packageDirNames.push(dirName);
});
// 保证 scope 只能为 all/package name/package dir name
const allScope = ["all", ...packageDirNames, ...packageNames];

module.exports = {
  extends: ["@commitlint/config-conventional"],
  rules: {
    "scope-enum": [2, "always", allScope],
  },
};

Примечание: здесь не нужно добавлять новыеprettierrc.js(корневой каталог уже существует) иeslintrc.js(каждый элемент уже существует).

добавлен корневой каталог.lintstagedrc 文件

.lintstagedrc

{
  "{apps,packages,features}/**/*.{js,jsx,ts,tsx}": [
    "eslint --fix --color",
    "prettier --write"
  ],
  "{apps,packages,features}/**/*.{css,less,md}": ["prettier --write"]
}

После завершения установки соответствующих зависимостей и написания конфигурации пропишем выполнение соответствующих команд вrushсередина.

Исправлятьcommon/config/rush/command-line.jsonв файлеcommandsполе.

{
  "commands": [
    {
      "name": "commitlint",
      "commandKind": "global",
      "summary": "Used by the commit-msg Git hook. This command invokes commitlint to lint commit message.",
      "autoinstallerName": "rush-lint",
      "shellCommand": "node common/autoinstallers/rush-lint/commit-lint.js"
    },
    {
      "name": "lint",
      "commandKind": "global",
      "summary": "Used by the pre-commit Git hook. This command invokes eslint to lint staged changes.",
      "autoinstallerName": "rush-lint",
      "shellCommand": "lint-staged"
    }
  ]
}

Наконец, будетrush commitlintтак же какrush lintДве командыcommit-msgтак же какpre-commitкрючок для вязки.common/git-hooksдобавить в каталогcommit-msgтак же какpre-commitсценарий.

commit-msg

#!/bin/sh

node common/scripts/install-run-rush.js commitlint || exit $? #++

pre-commit

#!/bin/sh

node common/scripts/install-run-rush.js lint || exit $? #++

Таким образом, требование выполняется.

Избегайте установки eslint и prettier глобально

После обработки в предыдущем разделеrush-lintустановлен в каталогеeslintтак же какprettierПосле этого нам не нужно устанавливать его глобально, нам просто нужно настроить VSCode.

{
  // ...
  "npm.packageManager": "pnpm",
  "eslint.packageManager": "pnpm",
  "eslint.nodePath": "common/autoinstallers/rush-lint/node_modules/eslint",
  "prettier.prettierPath": "common/autoinstallers/rush-lint/node_modules/prettier"
  // ...
}

приложение

Общие команды

yarn rush(x) detail
yarn install rush install Установить зависимости
yarn upgrade rush update срочные обновления зависимостей установки на основе файлов блокировки
rush update --full Полное обновление до последней версии, соответствующей package.json
yarn add package-name rush add -p package-name Номер установочной версии по умолчанию для добавления пряжи начинается с ^ и может принимать второстепенные обновления версии.
rush add Номер версии установки по умолчанию начинается с ~, принимаются только исправления
rush add может добиться того же эффекта, что и yarn add, добавив параметр --caret
rush add не может установить несколько пакетов одновременно
yarn add package-name --dev rush add -p package-name --dev -
yarn remove package-name - rush не предоставляет команду удаления
- rush build Выполняет скрипт сборки проекта с изменениями файлов (на основе git)
rush build -t @monorepo/app1 означает сборку только @monorepo/app1 и зависимых от него пакетов
Rush build -t @monorepo/app1 означает только зависимый пакет @monorepo/app1, не содержит сам себя
- rush rebuild Выполнение сценария сборки по умолчанию для всех проектов
пряжа ххх (пользовательский скрипт) rushx xxx (пользовательский скрипт) yarn xxx выполняет сценарии xxx (скрипты npm) в package.json в текущем каталоге
rushx xxx такой же. Вы можете напрямую запустить rushx для просмотра команд сценария, поддерживаемых текущим проектом.

Рабочий процесс

# 从 git 拉取最新变更
$ git pull

# 更新 NPM 依赖
$ rush update

# 重新打包 @monorepo/app1 依赖的项目(不含包其本身)
$ rush rebuild -T @monorepo/app1

# 进入指定项目目录
$ cd ./apps/app1

# 启动项目 ​
$ rushx start # or rushx dev

Справочная статья