Скрипты и рабочие пространства
Что это такое
Поле scripts в package.json называет команды, которые запускает проект — start, build, test, lint, — чтобы никому не приходилось помнить флаги. Рабочие пространства позволяют одному репозиторию держать несколько пакетов со своими манифестами, ставить их зависимости вместе и разрешать ссылки между ними тем же инструментом.
Почему это важно
Скрипты превращают сборку проекта в словарь: новый участник читает список скриптов в манифесте и знает, что набрать, а непрерывная интеграция вызывает те же имена, что и разработчик. Изменение флага тогда делается в одном месте, а не в странице вики и четырёх файлах CI, которые уже разошлись. Рабочие пространства решают проблему координации, которая появляется, как только в репозитории оказываются общая библиотека компонентов и два приложения: без них общий пакет приходится публиковать или связывать вручную, и каждое изменение превращается в танец на два репозитория.
Ключевые идеи
Скрипты — это интерфейс проекта
Каждая команда, нужная участнику, должна быть скриптом, и ничего лишнего: если сборке нужны три флага и переменная окружения, их место — в определении скрипта, а не в документе, который протухнет.
{
"scripts": {
"dev": "vite",
"build": "tsc --noEmit && vite build",
"test": "vitest run",
"lint": "eslint .",
"check": "npm run lint && npm run test && npm run build"
}
}
Единый скрипт check, который проходит те же ворота, что и непрерывная интеграция, делает сбой воспроизводимым локально.
Сцепляйте ворота, а не помните их
Порядок важен: проверка типов и линт идут до медленной сборки, тесты — до развёртывания. Скрипт check кодирует этот порядок один раз, и запускать его могут человек, хук перед push и конвейер.
В автоматизации предпочитайте npm ci, локально — npm install
npm ci удаляет установленные модули, ставит строго по lockfile и падает, если два файла расходятся. Этот сбой и есть цель: он ловит манифест, который отредактировали без соответствующей установки.
Рабочие пространства делают монорепозиторий рабочим
Объявление workspaces в корневом манифесте заставляет инструмент ставить все пакеты вместе, поднимать то, что можно поднять, и позволять одному пакету зависеть от другого по имени без публикации.
{
"name": "root",
"private": true,
"workspaces": ["packages/*", "apps/*"]
}
Корневой манифест должен оставаться private, чтобы его никогда не опубликовали случайно.
Подъём удобен и иногда удивляет
Единый каталог установки на верхнем уровне означает, что пакет может импортировать то, что он никогда не объявлял, просто потому что это случайно оказалось рядом. Локально это работает, однако при отдельной установке пакета ломается, поэтому общий пакет должен объявлять каждую используемую зависимость, даже если она уже есть в корне.
Скрипт можно запустить в конкретном рабочем пространстве
Запуск скрипта по рабочим пространствам — в одном названном пакете или во всех сразу — избавляет от смены каталога и оставляет команды в манифесте, где их можно прочитать.
Практика
Замените задокументированную команду скриптом
Найдите самую длинную команду в документации для участников вашего проекта и перенесите её в скрипт. Обновите документ, чтобы он называл скрипт.
Готово, когда: задокументированная команда сводится к одному имени скрипта и даёт тот же результат.
Вынесите дублирующуюся зависимость в рабочее пространство
Возьмите репозиторий, где два приложения копируют одну библиотеку компонентов, и перестройте его как рабочее пространство, в котором библиотека — пакет, от которого оба зависят по имени.
Готово, когда: изменение в библиотеке видно в обоих приложениях без отдельной пересборки, и ни одно приложение не повторяет исходный код библиотеки.
Частые ошибки
- Длинные команды только в документации — они расходятся, и никто не замечает, пока по ним не пойдёт новый человек; исправить: перенесите их в скрипты, а в документе назовите имя скрипта.
npm installв конвейере — lockfile переинтерпретируется, а не соблюдается, и сборка может отличаться от всех остальных машин; исправить: используйтеnpm ci.- Публикация корневого манифеста рабочего пространства — приватный пакет становится публичным; исправить: поставьте
private: trueв корне. - Опора на поднятые зависимости в общем пакете — это работает в репозитории и ломается при отдельной установке; исправить: объявляйте каждую зависимость, которую импортирует пакет.
- Обязательная переменная окружения, спрятанная в скрипте — сборку нельзя воспроизвести в другом месте; исправить: задокументируйте её или задайте безопасное значение по умолчанию.
Что читать дальше
- Пакеты и версии (02-frontend/04-package-management-npm/01-packages-and-versions) — разрешение версий и поведение lockfile, на которые опираются эти скрипты.
- CI/CD (06-cloud-devops/08-ci-cd) — где эти имена скриптов становятся шагами конвейера.
- Vite (02-frontend/09-vite) — инструмент, который вызывают большинство скриптов
devиbuild.